Actual source code: ex62.c

petsc-dev 2014-02-02
Report Typos and Errors
  1: static char help[] = "Stokes Problem in 2d and 3d with simplicial finite elements.\n\
  2: We solve the Stokes problem in a rectangular\n\
  3: domain, using a parallel unstructured mesh (DMPLEX) to discretize it.\n\n\n";

  5: /*
  6: The isoviscous Stokes problem, which we discretize using the finite
  7: element method on an unstructured mesh. The weak form equations are

  9:   < \nabla v, \nabla u + {\nabla u}^T > - < \nabla\cdot v, p > + < v, f > = 0
 10:   < q, \nabla\cdot v >                                                    = 0

 12: We start with homogeneous Dirichlet conditions. We will expand this as the set
 13: of test problems is developed.

 15: Discretization:

 17: We use PetscFE to generate a tabulation of the finite element basis functions
 18: at quadrature points. We can currently generate an arbitrary order Lagrange
 19: element.

 21: Field Data:

 23:   DMPLEX data is organized by point, and the closure operation just stacks up the
 24: data from each sieve point in the closure. Thus, for a P_2-P_1 Stokes element, we
 25: have

 27:   cl{e} = {f e_0 e_1 e_2 v_0 v_1 v_2}
 28:   x     = [u_{e_0} v_{e_0} u_{e_1} v_{e_1} u_{e_2} v_{e_2} u_{v_0} v_{v_0} p_{v_0} u_{v_1} v_{v_1} p_{v_1} u_{v_2} v_{v_2} p_{v_2}]

 30: The problem here is that we would like to loop over each field separately for
 31: integration. Therefore, the closure visitor in DMPlexVecGetClosure() reorders
 32: the data so that each field is contiguous

 34:   x'    = [u_{e_0} v_{e_0} u_{e_1} v_{e_1} u_{e_2} v_{e_2} u_{v_0} v_{v_0} u_{v_1} v_{v_1} u_{v_2} v_{v_2} p_{v_0} p_{v_1} p_{v_2}]

 36: Likewise, DMPlexVecSetClosure() takes data partitioned by field, and correctly
 37: puts it into the Sieve ordering.

 39: Next Steps:

 41: - Refine and show convergence of correct order automatically (use femTest.py)
 42: - Fix InitialGuess for arbitrary disc (means making dual application work again)
 43: - Redo slides from GUCASTutorial for this new example

 45: For tensor product meshes, see SNES ex67, ex72
 46: */

 48: #include <petscdmplex.h>
 49: #include <petscdt.h>
 50: #include <petscfe.h>
 51: #include <petscsnes.h>

 53: #define NUM_FIELDS 2
 54: PetscInt spatialDim = 0;

 56: typedef enum {NEUMANN, DIRICHLET} BCType;
 57: typedef enum {RUN_FULL, RUN_TEST} RunType;

 59: typedef struct {
 60:   PetscFEM      fem;               /* REQUIRED to use DMPlexComputeResidualFEM() */
 61:   PetscInt      debug;             /* The debugging level */
 62:   PetscMPIInt   rank;              /* The process rank */
 63:   PetscMPIInt   numProcs;          /* The number of processes */
 64:   RunType       runType;           /* Whether to run tests, or solve the full problem */
 65:   PetscBool     jacobianMF;        /* Whether to calculate the Jacobian action on the fly */
 66:   PetscLogEvent createMeshEvent;
 67:   PetscBool     showInitial, showSolution;
 68:   /* Domain and mesh definition */
 69:   PetscInt      dim;               /* The topological mesh dimension */
 70:   PetscBool     interpolate;       /* Generate intermediate mesh elements */
 71:   PetscReal     refinementLimit;   /* The largest allowable cell volume */
 72:   char          partitioner[2048]; /* The graph partitioner */
 73:   /* GPU partitioning */
 74:   PetscInt      numBatches;        /* The number of cell batches per kernel */
 75:   PetscInt      numBlocks;         /* The number of concurrent blocks per kernel */
 76:   /* Element quadrature */
 77:   PetscFE       fe[NUM_FIELDS];    /* Element definitions for each field */
 78:   PetscQuadrature q[NUM_FIELDS];
 79:   /* Problem definition */
 80:   void (*f0Funcs[NUM_FIELDS])(const PetscScalar u[], const PetscScalar gradU[], const PetscScalar a[], const PetscScalar gradA[], const PetscReal x[], PetscScalar f0[]); /* f0_u(x,y,z), and f0_p(x,y,z) */
 81:   void (*f1Funcs[NUM_FIELDS])(const PetscScalar u[], const PetscScalar gradU[], const PetscScalar a[], const PetscScalar gradA[], const PetscReal x[], PetscScalar f1[]); /* f1_u(x,y,z), and f1_p(x,y,z) */
 82:   void (*g0Funcs[NUM_FIELDS*NUM_FIELDS])(const PetscScalar u[], const PetscScalar gradU[], const PetscScalar a[], const PetscScalar gradA[], const PetscReal x[], PetscScalar g0[]); /* g0_uu(x,y,z), g0_up(x,y,z), g0_pu(x,y,z), and g0_pp(x,y,z) */
 83:   void (*g1Funcs[NUM_FIELDS*NUM_FIELDS])(const PetscScalar u[], const PetscScalar gradU[], const PetscScalar a[], const PetscScalar gradA[], const PetscReal x[], PetscScalar g1[]); /* g1_uu(x,y,z), g1_up(x,y,z), g1_pu(x,y,z), and g1_pp(x,y,z) */
 84:   void (*g2Funcs[NUM_FIELDS*NUM_FIELDS])(const PetscScalar u[], const PetscScalar gradU[], const PetscScalar a[], const PetscScalar gradA[], const PetscReal x[], PetscScalar g2[]); /* g2_uu(x,y,z), g2_up(x,y,z), g2_pu(x,y,z), and g2_pp(x,y,z) */
 85:   void (*g3Funcs[NUM_FIELDS*NUM_FIELDS])(const PetscScalar u[], const PetscScalar gradU[], const PetscScalar a[], const PetscScalar gradA[], const PetscReal x[], PetscScalar g3[]); /* g3_uu(x,y,z), g3_up(x,y,z), g3_pu(x,y,z), and g3_pp(x,y,z) */
 86:   void (**exactFuncs)(const PetscReal x[], PetscScalar *u, void *ctx); /* The exact solution function u(x,y,z), v(x,y,z), and p(x,y,z) */
 87:   void (*initialGuess[NUM_FIELDS])(const PetscReal x[], PetscScalar *u, void* ctx);
 88:   BCType bcType;
 89: } AppCtx;

 91: void zero_1d(const PetscReal coords[], PetscScalar *u, void *ctx)
 92: {
 93:   u[0] = 0.0;
 94: }
 95: void zero_2d(const PetscReal coords[], PetscScalar *u, void *ctx)
 96: {
 97:   u[0] = 0.0; u[1] = 0.0;
 98: }
 99: void zero_3d(const PetscReal coords[], PetscScalar *u, void *ctx)
100: {
101:   u[0] = 0.0; u[1] = 0.0; u[2] = 0.0;
102: }

104: /*
105:   In 2D we use exact solution:

107:     u = x^2 + y^2
108:     v = 2 x^2 - 2xy
109:     p = x + y - 1
110:     f_x = f_y = 3

112:   so that

114:     -\Delta u + \nabla p + f = <-4, -4> + <1, 1> + <3, 3> = 0
115:     \nabla \cdot u           = 2x - 2x                    = 0
116: */
117: void quadratic_u_2d(const PetscReal x[], PetscScalar *u, void *ctx)
118: {
119:   u[0] = x[0]*x[0] + x[1]*x[1];
120:   u[1] = 2.0*x[0]*x[0] - 2.0*x[0]*x[1];
121: }

123: void linear_p_2d(const PetscReal x[], PetscScalar *p, void *ctx)
124: {
125:   *p = x[0] + x[1] - 1.0;
126: }

128: void f0_u(const PetscScalar u[], const PetscScalar gradU[], const PetscScalar a[], const PetscScalar gradA[], const PetscReal x[], PetscScalar f0[])
129: {
130:   const PetscInt Ncomp = spatialDim;
131:   PetscInt       comp;

133:   for (comp = 0; comp < Ncomp; ++comp) f0[comp] = 3.0;
134: }

136: /* gradU[comp*dim+d] = {u_x, u_y, v_x, v_y} or {u_x, u_y, u_z, v_x, v_y, v_z, w_x, w_y, w_z}
137:    u[Ncomp]          = {p} */
138: void f1_u(const PetscScalar u[], const PetscScalar gradU[], const PetscScalar a[], const PetscScalar gradA[], const PetscReal x[], PetscScalar f1[])
139: {
140:   const PetscInt dim   = spatialDim;
141:   const PetscInt Ncomp = spatialDim;
142:   PetscInt       comp, d;

144:   for (comp = 0; comp < Ncomp; ++comp) {
145:     for (d = 0; d < dim; ++d) {
146:       /* f1[comp*dim+d] = 0.5*(gradU[comp*dim+d] + gradU[d*dim+comp]); */
147:       f1[comp*dim+d] = gradU[comp*dim+d];
148:     }
149:     f1[comp*dim+comp] -= u[Ncomp];
150:   }
151: }

153: /* gradU[comp*dim+d] = {u_x, u_y, v_x, v_y} or {u_x, u_y, u_z, v_x, v_y, v_z, w_x, w_y, w_z} */
154: void f0_p(const PetscScalar u[], const PetscScalar gradU[], const PetscScalar a[], const PetscScalar gradA[], const PetscReal x[], PetscScalar f0[])
155: {
156:   const PetscInt dim = spatialDim;
157:   PetscInt       d;

159:   f0[0] = 0.0;
160:   for (d = 0; d < dim; ++d) f0[0] += gradU[d*dim+d];
161: }

163: void f1_p(const PetscScalar u[], const PetscScalar gradU[], const PetscScalar a[], const PetscScalar gradA[], const PetscReal x[], PetscScalar f1[])
164: {
165:   const PetscInt dim = spatialDim;
166:   PetscInt       d;

168:   for (d = 0; d < dim; ++d) f1[d] = 0.0;
169: }

171: /* < q, \nabla\cdot v >
172:    NcompI = 1, NcompJ = dim */
173: void g1_pu(const PetscScalar u[], const PetscScalar gradU[], const PetscScalar a[], const PetscScalar gradA[], const PetscReal x[], PetscScalar g1[])
174: {
175:   const PetscInt dim = spatialDim;
176:   PetscInt       d;

178:   for (d = 0; d < dim; ++d) g1[d*dim+d] = 1.0; /* \frac{\partial\phi^{u_d}}{\partial x_d} */
179: }

181: /* -< \nabla\cdot v, p >
182:     NcompI = dim, NcompJ = 1 */
183: void g2_up(const PetscScalar u[], const PetscScalar gradU[], const PetscScalar a[], const PetscScalar gradA[], const PetscReal x[], PetscScalar g2[])
184: {
185:   const PetscInt dim = spatialDim;
186:   PetscInt       d;

188:   for (d = 0; d < dim; ++d) g2[d*dim+d] = -1.0; /* \frac{\partial\psi^{u_d}}{\partial x_d} */
189: }

191: /* < \nabla v, \nabla u + {\nabla u}^T >
192:    This just gives \nabla u, give the perdiagonal for the transpose */
193: void g3_uu(const PetscScalar u[], const PetscScalar gradU[], const PetscScalar a[], const PetscScalar gradA[], const PetscReal x[], PetscScalar g3[])
194: {
195:   const PetscInt dim   = spatialDim;
196:   const PetscInt Ncomp = spatialDim;
197:   PetscInt       compI, d;

199:   for (compI = 0; compI < Ncomp; ++compI) {
200:     for (d = 0; d < dim; ++d) {
201:       g3[((compI*Ncomp+compI)*dim+d)*dim+d] = 1.0;
202:     }
203:   }
204: }

206: /*
207:   In 3D we use exact solution:

209:     u = x^2 + y^2
210:     v = y^2 + z^2
211:     w = x^2 + y^2 - 2(x+y)z
212:     p = x + y + z - 3/2
213:     f_x = f_y = f_z = 3

215:   so that

217:     -\Delta u + \nabla p + f = <-4, -4, -4> + <1, 1, 1> + <3, 3, 3> = 0
218:     \nabla \cdot u           = 2x + 2y - 2(x + y)                   = 0
219: */
220: void quadratic_u_3d(const PetscReal x[], PetscScalar *u, void *ctx)
221: {
222:   u[0] = x[0]*x[0] + x[1]*x[1];
223:   u[1] = x[1]*x[1] + x[2]*x[2];
224:   u[2] = x[0]*x[0] + x[1]*x[1] - 2.0*(x[0] + x[1])*x[2];
225: }

227: void linear_p_3d(const PetscReal x[], PetscScalar *p, void *ctx)
228: {
229:   *p = x[0] + x[1] + x[2] - 1.5;
230: }

234: PetscErrorCode ProcessOptions(MPI_Comm comm, AppCtx *options)
235: {
236:   const char    *bcTypes[2]  = {"neumann", "dirichlet"};
237:   const char    *runTypes[2] = {"full", "test"};
238:   PetscInt       bc, run;

242:   options->debug           = 0;
243:   options->runType         = RUN_FULL;
244:   options->dim             = 2;
245:   options->interpolate     = PETSC_FALSE;
246:   options->refinementLimit = 0.0;
247:   options->bcType          = DIRICHLET;
248:   options->numBatches      = 1;
249:   options->numBlocks       = 1;
250:   options->jacobianMF      = PETSC_FALSE;
251:   options->showInitial     = PETSC_FALSE;
252:   options->showSolution    = PETSC_TRUE;

254:   options->fem.f0Funcs = (void (**)(const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscReal[], PetscScalar[])) &options->f0Funcs;
255:   options->fem.f1Funcs = (void (**)(const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscReal[], PetscScalar[])) &options->f1Funcs;
256:   options->fem.g0Funcs = (void (**)(const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscReal[], PetscScalar[])) &options->g0Funcs;
257:   options->fem.g1Funcs = (void (**)(const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscReal[], PetscScalar[])) &options->g1Funcs;
258:   options->fem.g2Funcs = (void (**)(const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscReal[], PetscScalar[])) &options->g2Funcs;
259:   options->fem.g3Funcs = (void (**)(const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscReal[], PetscScalar[])) &options->g3Funcs;

261:   MPI_Comm_size(comm, &options->numProcs);
262:   MPI_Comm_rank(comm, &options->rank);
263:   PetscOptionsBegin(comm, "", "Stokes Problem Options", "DMPLEX");
264:   PetscOptionsInt("-debug", "The debugging level", "ex62.c", options->debug, &options->debug, NULL);
265:   run  = options->runType;
266:   PetscOptionsEList("-run_type", "The run type", "ex62.c", runTypes, 2, runTypes[options->runType], &run, NULL);

268:   options->runType = (RunType) run;

270:   PetscOptionsInt("-dim", "The topological mesh dimension", "ex62.c", options->dim, &options->dim, NULL);
271:   spatialDim = options->dim;
272:   PetscOptionsBool("-interpolate", "Generate intermediate mesh elements", "ex62.c", options->interpolate, &options->interpolate, NULL);
273:   PetscOptionsReal("-refinement_limit", "The largest allowable cell volume", "ex62.c", options->refinementLimit, &options->refinementLimit, NULL);
274:   PetscStrcpy(options->partitioner, "chaco");
275:   PetscOptionsString("-partitioner", "The graph partitioner", "pflotran.cxx", options->partitioner, options->partitioner, 2048, NULL);
276:   bc   = options->bcType;
277:   PetscOptionsEList("-bc_type","Type of boundary condition","ex62.c",bcTypes,2,bcTypes[options->bcType],&bc,NULL);

279:   options->bcType = (BCType) bc;

281:   PetscOptionsInt("-gpu_batches", "The number of cell batches per kernel", "ex62.c", options->numBatches, &options->numBatches, NULL);
282:   PetscOptionsInt("-gpu_blocks", "The number of concurrent blocks per kernel", "ex62.c", options->numBlocks, &options->numBlocks, NULL);
283:   PetscOptionsBool("-jacobian_mf", "Calculate the action of the Jacobian on the fly", "ex62.c", options->jacobianMF, &options->jacobianMF, NULL);
284:   PetscOptionsBool("-show_initial", "Output the initial guess for verification", "ex62.c", options->showInitial, &options->showInitial, NULL);
285:   PetscOptionsBool("-show_solution", "Output the solution for verification", "ex62.c", options->showSolution, &options->showSolution, NULL);
286:   PetscOptionsEnd();

288:   PetscLogEventRegister("CreateMesh", DM_CLASSID, &options->createMeshEvent);
289:   return(0);
290: }

294: PetscErrorCode DMVecViewLocal(DM dm, Vec v, PetscViewer viewer)
295: {
296:   Vec            lv;
297:   PetscInt       p;
298:   PetscMPIInt    rank, numProcs;

302:   MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank);
303:   MPI_Comm_size(PetscObjectComm((PetscObject)dm), &numProcs);
304:   DMGetLocalVector(dm, &lv);
305:   DMGlobalToLocalBegin(dm, v, INSERT_VALUES, lv);
306:   DMGlobalToLocalEnd(dm, v, INSERT_VALUES, lv);
307:   PetscPrintf(PETSC_COMM_WORLD, "Local function\n");
308:   for (p = 0; p < numProcs; ++p) {
309:     if (p == rank) {VecView(lv, PETSC_VIEWER_STDOUT_SELF);}
310:     PetscBarrier((PetscObject) dm);
311:   }
312:   DMRestoreLocalVector(dm, &lv);
313:   return(0);
314: }

318: PetscErrorCode CreateMesh(MPI_Comm comm, AppCtx *user, DM *dm)
319: {
320:   DMLabel        label;
321:   PetscInt       dim             = user->dim;
322:   PetscBool      interpolate     = user->interpolate;
323:   PetscReal      refinementLimit = user->refinementLimit;
324:   const char     *partitioner    = user->partitioner;

328:   PetscLogEventBegin(user->createMeshEvent,0,0,0,0);
329:   DMPlexCreateBoxMesh(comm, dim, interpolate, dm);
330:   DMPlexGetLabel(*dm, "marker", &label);
331:   if (label) {DMPlexLabelComplete(*dm, label);}
332:   {
333:     DM refinedMesh     = NULL;
334:     DM distributedMesh = NULL;

336:     /* Refine mesh using a volume constraint */
337:     DMPlexSetRefinementLimit(*dm, refinementLimit);
338:     DMRefine(*dm, comm, &refinedMesh);
339:     if (refinedMesh) {
340:       DMDestroy(dm);
341:       *dm  = refinedMesh;
342:     }
343:     /* Distribute mesh over processes */
344:     DMPlexDistribute(*dm, partitioner, 0, NULL, &distributedMesh);
345:     if (distributedMesh) {
346:       DMDestroy(dm);
347:       *dm  = distributedMesh;
348:     }
349:   }
350:   DMSetFromOptions(*dm);
351:   PetscLogEventEnd(user->createMeshEvent,0,0,0,0);
352:   return(0);
353: }

357: PetscErrorCode SetupElement(DM dm, AppCtx *user)
358: {
359:   const PetscInt  dim = user->dim, numFields = 2;
360:   const char     *prefix[2] = {"vel_", "pres_"};
361:   PetscInt        qorder    = 0, f;
362:   PetscErrorCode  ierr;

365:   for (f = 0; f < numFields; ++f) {
366:     PetscFE         fem;
367:     DM              K;
368:     PetscSpace      P;
369:     PetscDualSpace  Q;
370:     PetscInt        order;

372:     /* Create space */
373:     PetscSpaceCreate(PetscObjectComm((PetscObject) dm), &P);
374:     PetscObjectSetOptionsPrefix((PetscObject) P, prefix[f]);
375:     PetscSpaceSetFromOptions(P);
376:     PetscSpacePolynomialSetNumVariables(P, dim);
377:     PetscSpaceSetUp(P);
378:     PetscSpaceGetOrder(P, &order);
379:     qorder = PetscMax(qorder, order);
380:     /* Create dual space */
381:     PetscDualSpaceCreate(PetscObjectComm((PetscObject) dm), &Q);
382:     PetscObjectSetOptionsPrefix((PetscObject) Q, prefix[f]);
383:     PetscDualSpaceCreateReferenceCell(Q, dim, PETSC_TRUE, &K);
384:     PetscDualSpaceSetDM(Q, K);
385:     DMDestroy(&K);
386:     PetscDualSpaceSetOrder(Q, order);
387:     PetscDualSpaceSetFromOptions(Q);
388:     PetscDualSpaceSetUp(Q);
389:     /* Create element */
390:     PetscFECreate(PetscObjectComm((PetscObject) dm), &fem);
391:     PetscObjectSetOptionsPrefix((PetscObject) fem, prefix[f]);
392:     PetscFESetFromOptions(fem);
393:     PetscFESetBasisSpace(fem, P);
394:     PetscFESetDualSpace(fem, Q);
395:     PetscFESetNumComponents(fem, f ? 1 : dim);

397:     PetscSpaceDestroy(&P);
398:     PetscDualSpaceDestroy(&Q);
399:     user->fe[f] = fem;
400:   }
401:   for (f = 0; f < numFields; ++f) {
402:     PetscQuadrature q;

404:     /* Create quadrature */
405:     PetscDTGaussJacobiQuadrature(dim, qorder, -1.0, 1.0, &q);
406:     PetscFESetQuadrature(user->fe[f], q);
407:   }
408:   user->fem.fe    = user->fe;
409:   user->fem.feAux = NULL;
410:   return(0);
411: }

415: PetscErrorCode DestroyElement(AppCtx *user)
416: {
417:   PetscInt       numFields = 2, f;

421:   for (f = 0; f < numFields; ++f) {
422:     PetscFEDestroy(&user->fe[f]);
423:   }
424:   return(0);
425: }

429: /*
430:   There is a problem here with uninterpolated meshes. The index in numDof[] is not dimension in this case,
431:   but sieve depth.
432: */
433: PetscErrorCode SetupSection(DM dm, AppCtx *user)
434: {
435:   PetscSection    section;
436:   const PetscInt  numFields           = NUM_FIELDS;
437:   PetscInt        dim                 = user->dim;
438:   PetscInt        numBC               = 0;
439:   PetscInt        bcFields[1]         = {0};
440:   IS              bcPoints[1]         = {NULL};
441:   PetscInt        numComp[NUM_FIELDS];
442:   const PetscInt *numFieldDof[NUM_FIELDS];
443:   PetscInt       *numDof;
444:   PetscInt        f, d;
445:   PetscErrorCode  ierr;

448:   PetscFEGetNumComponents(user->fe[0], &numComp[0]);
449:   PetscFEGetNumComponents(user->fe[1], &numComp[1]);
450:   PetscFEGetNumDof(user->fe[0], &numFieldDof[0]);
451:   PetscFEGetNumDof(user->fe[1], &numFieldDof[1]);
452:   PetscMalloc1(NUM_FIELDS*(dim+1), &numDof);
453:   for (f = 0; f < NUM_FIELDS; ++f) {
454:     for (d = 0; d <= dim; ++d) {
455:       numDof[f*(dim+1)+d] = numFieldDof[f][d];
456:     }
457:   }
458:   for (f = 0; f < numFields; ++f) {
459:     for (d = 1; d < dim; ++d) {
460:       if ((numDof[f*(dim+1)+d] > 0) && !user->interpolate) SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Mesh must be interpolated when unknowns are specified on edges or faces.");
461:     }
462:   }
463:   if (user->bcType == DIRICHLET) {
464:     numBC = 1;
465:     DMPlexGetStratumIS(dm, "marker", 1, &bcPoints[0]);
466:   }
467:   DMPlexCreateSection(dm, dim, numFields, numComp, numDof, numBC, bcFields, bcPoints, &section);
468:   PetscSectionSetFieldName(section, 0, "velocity");
469:   PetscSectionSetFieldName(section, 1, "pressure");
470:   DMSetDefaultSection(dm, section);
471:   PetscSectionDestroy(&section);
472:   if (user->bcType == DIRICHLET) {
473:     ISDestroy(&bcPoints[0]);
474:   }
475:   PetscFree(numDof);
476:   return(0);
477: }

481: PetscErrorCode SetupExactSolution(DM dm, AppCtx *user)
482: {
483:   PetscFEM *fem = &user->fem;

486:   fem->f0Funcs[0] = f0_u;
487:   fem->f0Funcs[1] = f0_p;
488:   fem->f1Funcs[0] = f1_u;
489:   fem->f1Funcs[1] = f1_p;
490:   fem->g0Funcs[0] = NULL;
491:   fem->g0Funcs[1] = NULL;
492:   fem->g0Funcs[2] = NULL;
493:   fem->g0Funcs[3] = NULL;
494:   fem->g1Funcs[0] = NULL;
495:   fem->g1Funcs[1] = NULL;
496:   fem->g1Funcs[2] = g1_pu;      /* < q, \nabla\cdot v > */
497:   fem->g1Funcs[3] = NULL;
498:   fem->g2Funcs[0] = NULL;
499:   fem->g2Funcs[1] = g2_up;      /* < \nabla\cdot v, p > */
500:   fem->g2Funcs[2] = NULL;
501:   fem->g2Funcs[3] = NULL;
502:   fem->g3Funcs[0] = g3_uu;      /* < \nabla v, \nabla u + {\nabla u}^T > */
503:   fem->g3Funcs[1] = NULL;
504:   fem->g3Funcs[2] = NULL;
505:   fem->g3Funcs[3] = NULL;
506:   switch (user->dim) {
507:   case 2:
508:     user->exactFuncs[0] = quadratic_u_2d;
509:     user->exactFuncs[1] = linear_p_2d;
510:     user->initialGuess[0] = zero_2d;
511:     user->initialGuess[1] = zero_1d;
512:     break;
513:   case 3:
514:     user->exactFuncs[0] = quadratic_u_3d;
515:     user->exactFuncs[1] = linear_p_3d;
516:     user->initialGuess[0] = zero_3d;
517:     user->initialGuess[1] = zero_1d;
518:     break;
519:   default:
520:     SETERRQ1(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Invalid dimension %d", user->dim);
521:   }
522:   return(0);
523: }

527: PetscErrorCode CreatePressureNullSpace(DM dm, AppCtx *user, MatNullSpace *nullSpace)
528: {
529:   Vec            vec, localVec;

533:   DMGetGlobalVector(dm, &vec);
534:   DMGetLocalVector(dm, &localVec);
535:   VecSet(vec,  0.0);
536:   /* Put a constant in for all pressures
537:      Could change this to project the constant function onto the pressure space (when that is finished) */
538:   {
539:     PetscSection section;
540:     PetscInt     pStart, pEnd, p;
541:     PetscScalar  *a;

543:     DMGetDefaultSection(dm, &section);
544:     PetscSectionGetChart(section, &pStart, &pEnd);
545:     VecGetArray(localVec, &a);
546:     for (p = pStart; p < pEnd; ++p) {
547:       PetscInt fDim, off, d;

549:       PetscSectionGetFieldDof(section, p, 1, &fDim);
550:       PetscSectionGetFieldOffset(section, p, 1, &off);
551:       for (d = 0; d < fDim; ++d) a[off+d] = 1.0;
552:     }
553:     VecRestoreArray(localVec, &a);
554:   }
555:   DMLocalToGlobalBegin(dm, localVec, INSERT_VALUES, vec);
556:   DMLocalToGlobalEnd(dm, localVec, INSERT_VALUES, vec);
557:   DMRestoreLocalVector(dm, &localVec);
558:   VecNormalize(vec, NULL);
559:   if (user->debug) {
560:     PetscPrintf(PetscObjectComm((PetscObject)dm), "Pressure Null Space\n");
561:     VecView(vec, PETSC_VIEWER_STDOUT_WORLD);
562:   }
563:   MatNullSpaceCreate(PetscObjectComm((PetscObject)dm), PETSC_FALSE, 1, &vec, nullSpace);
564:   DMRestoreGlobalVector(dm, &vec);
565:   /* New style for field null spaces */
566:   {
567:     PetscObject  pressure;
568:     MatNullSpace nullSpacePres;

570:     DMGetField(dm, 1, &pressure);
571:     MatNullSpaceCreate(PetscObjectComm(pressure), PETSC_TRUE, 0, NULL, &nullSpacePres);
572:     PetscObjectCompose(pressure, "nullspace", (PetscObject) nullSpacePres);
573:     MatNullSpaceDestroy(&nullSpacePres);
574:   }
575:   return(0);
576: }

580: /*
581:   FormJacobianAction - Form the global Jacobian action Y = JX from the global input X

583:   Input Parameters:
584: + mat - The Jacobian shell matrix
585: - X  - Global input vector

587:   Output Parameter:
588: . Y  - Local output vector

590:   Note:
591:   We form the residual one batch of elements at a time. This allows us to offload work onto an accelerator,
592:   like a GPU, or vectorize on a multicore machine.

594: .seealso: FormJacobianActionLocal()
595: */
596: PetscErrorCode FormJacobianAction(Mat J, Vec X,  Vec Y)
597: {
598:   JacActionCtx   *ctx;
599:   DM             dm;
600:   Vec            localX, localY;
601:   PetscInt       N, n;

605: #if 0
606:   /* Needs petscimpl.h */
610: #endif
611:   MatShellGetContext(J, &ctx);
612:   dm   = ctx->dm;

614:   /* determine whether X = localX */
615:   DMGetLocalVector(dm, &localX);
616:   DMGetLocalVector(dm, &localY);
617:   VecGetSize(X, &N);
618:   VecGetSize(localX, &n);

620:   if (n != N) { /* X != localX */
621:     VecSet(localX, 0.0);
622:     DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX);
623:     DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX);
624:   } else {
625:     DMRestoreLocalVector(dm, &localX);
626:     localX = X;
627:   }
628:   DMPlexComputeJacobianActionFEM(dm, J, localX, localY, ctx->user);
629:   if (n != N) {
630:     DMRestoreLocalVector(dm, &localX);
631:   }
632:   VecSet(Y, 0.0);
633:   DMLocalToGlobalBegin(dm, localY, ADD_VALUES, Y);
634:   DMLocalToGlobalEnd(dm, localY, ADD_VALUES, Y);
635:   DMRestoreLocalVector(dm, &localY);
636:   if (0) {
637:     Vec       r;
638:     PetscReal norm;

640:     VecDuplicate(X, &r);
641:     MatMult(ctx->J, X, r);
642:     VecAXPY(r, -1.0, Y);
643:     VecNorm(r, NORM_2, &norm);
644:     if (norm > 1.0e-8) {
645:       PetscPrintf(PETSC_COMM_WORLD, "Jacobian Action Input:\n");
646:       VecView(X, PETSC_VIEWER_STDOUT_WORLD);
647:       PetscPrintf(PETSC_COMM_WORLD, "Jacobian Action Result:\n");
648:       VecView(Y, PETSC_VIEWER_STDOUT_WORLD);
649:       PetscPrintf(PETSC_COMM_WORLD, "Difference:\n");
650:       VecView(r, PETSC_VIEWER_STDOUT_WORLD);
651:       SETERRQ1(PetscObjectComm((PetscObject)J), PETSC_ERR_ARG_WRONG, "The difference with assembled multiply is too large %g", norm);
652:     }
653:     VecDestroy(&r);
654:   }
655:   return(0);
656: }

660: int main(int argc, char **argv)
661: {
662:   SNES           snes;                 /* nonlinear solver */
663:   DM             dm;                   /* problem definition */
664:   Vec            u,r;                  /* solution, residual vectors */
665:   Mat            A,J;                  /* Jacobian matrix */
666:   MatNullSpace   nullSpace;            /* May be necessary for pressure */
667:   AppCtx         user;                 /* user-defined work context */
668:   JacActionCtx   userJ;                /* context for Jacobian MF action */
669:   PetscInt       its;                  /* iterations for convergence */
670:   PetscReal      error         = 0.0;  /* L_2 error in the solution */
671:   PetscInt       numComponents = 0, f;

674:   PetscInitialize(&argc, &argv, NULL, help);
675:   ProcessOptions(PETSC_COMM_WORLD, &user);
676:   SNESCreate(PETSC_COMM_WORLD, &snes);
677:   CreateMesh(PETSC_COMM_WORLD, &user, &dm);
678:   SNESSetDM(snes, dm);

680:   SetupElement(dm, &user);
681:   for (f = 0; f < NUM_FIELDS; ++f) {
682:     PetscInt numComp;
683:     PetscFEGetNumComponents(user.fe[f], &numComp);
684:     numComponents += numComp;
685:   }
686:   PetscMalloc(NUM_FIELDS * sizeof(void (*)(const PetscReal[], PetscScalar *, void *)), &user.exactFuncs);
687:   user.fem.bcFuncs = user.exactFuncs;
688:   user.fem.bcCtxs  = NULL;
689:   SetupExactSolution(dm, &user);
690:   SetupSection(dm, &user);
691:   DMPlexCreateClosureIndex(dm, NULL);

693:   DMCreateGlobalVector(dm, &u);
694:   VecDuplicate(u, &r);

696:   DMSetMatType(dm,MATAIJ);
697:   DMCreateMatrix(dm, &J);
698:   if (user.jacobianMF) {
699:     PetscInt M, m, N, n;

701:     MatGetSize(J, &M, &N);
702:     MatGetLocalSize(J, &m, &n);
703:     MatCreate(PETSC_COMM_WORLD, &A);
704:     MatSetSizes(A, m, n, M, N);
705:     MatSetType(A, MATSHELL);
706:     MatSetUp(A);
707:     MatShellSetOperation(A, MATOP_MULT, (void (*)(void))FormJacobianAction);

709:     userJ.dm   = dm;
710:     userJ.J    = J;
711:     userJ.user = &user;

713:     DMCreateLocalVector(dm, &userJ.u);
714:     DMPlexProjectFunctionLocal(dm, user.fe, user.exactFuncs, NULL, INSERT_BC_VALUES, userJ.u);
715:     MatShellSetContext(A, &userJ);
716:   } else {
717:     A = J;
718:   }
719:   CreatePressureNullSpace(dm, &user, &nullSpace);
720:   MatSetNullSpace(J, nullSpace);
721:   if (A != J) {
722:     MatSetNullSpace(A, nullSpace);
723:   }

725:   DMSNESSetFunctionLocal(dm,  (PetscErrorCode (*)(DM,Vec,Vec,void*))DMPlexComputeResidualFEM,&user);
726:   DMSNESSetJacobianLocal(dm,  (PetscErrorCode (*)(DM,Vec,Mat,Mat,MatStructure*,void*))DMPlexComputeJacobianFEM,&user);
727:   SNESSetJacobian(snes, A, J, NULL, NULL);

729:   SNESSetFromOptions(snes);

731:   DMPlexProjectFunction(dm, user.fe, user.exactFuncs, NULL, INSERT_ALL_VALUES, u);
732:   if (user.showInitial) {DMVecViewLocal(dm, u, PETSC_VIEWER_STDOUT_SELF);}
733:   if (user.runType == RUN_FULL) {
734:     DMPlexProjectFunction(dm, user.fe, user.initialGuess, NULL, INSERT_VALUES, u);
735:     if (user.showInitial) {DMVecViewLocal(dm, u, PETSC_VIEWER_STDOUT_SELF);}
736:     if (user.debug) {
737:       PetscPrintf(PETSC_COMM_WORLD, "Initial guess\n");
738:       VecView(u, PETSC_VIEWER_STDOUT_WORLD);
739:     }
740:     SNESSolve(snes, NULL, u);
741:     SNESGetIterationNumber(snes, &its);
742:     PetscPrintf(PETSC_COMM_WORLD, "Number of SNES iterations = %D\n", its);
743:     DMPlexComputeL2Diff(dm, user.fe, user.exactFuncs, NULL, u, &error);
744:     PetscPrintf(PETSC_COMM_WORLD, "L_2 Error: %.3g\n", error);
745:     if (user.showSolution) {
746:       PetscPrintf(PETSC_COMM_WORLD, "Solution\n");
747:       VecChop(u, 3.0e-9);
748:       VecView(u, PETSC_VIEWER_STDOUT_WORLD);
749:     }
750:   } else {
751:     PetscReal res = 0.0;

753:     /* Check discretization error */
754:     PetscPrintf(PETSC_COMM_WORLD, "Initial guess\n");
755:     VecView(u, PETSC_VIEWER_STDOUT_WORLD);
756:     DMPlexComputeL2Diff(dm, user.fe, user.exactFuncs, NULL, u, &error);
757:     if (error >= 1.0e-11) {
758:       PetscPrintf(PETSC_COMM_WORLD, "L_2 Error: %g\n", error);
759:     } else {
760:       PetscPrintf(PETSC_COMM_WORLD, "L_2 Error: < 1.0e-11\n", error);
761:     }
762:     /* Check residual */
763:     SNESComputeFunction(snes, u, r);
764:     PetscPrintf(PETSC_COMM_WORLD, "Initial Residual\n");
765:     VecChop(r, 1.0e-10);
766:     VecView(r, PETSC_VIEWER_STDOUT_WORLD);
767:     VecNorm(r, NORM_2, &res);
768:     PetscPrintf(PETSC_COMM_WORLD, "L_2 Residual: %g\n", res);
769:     /* Check Jacobian */
770:     {
771:       Vec          b;
772:       MatStructure flag;
773:       PetscBool    isNull;

775:       SNESComputeJacobian(snes, u, &A, &A, &flag);
776:       MatNullSpaceTest(nullSpace, J, &isNull);
777:       if (!isNull) SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_PLIB, "The null space calculated for the system operator is invalid.");
778:       VecDuplicate(u, &b);
779:       VecSet(r, 0.0);
780:       SNESComputeFunction(snes, r, b);
781:       MatMult(A, u, r);
782:       VecAXPY(r, 1.0, b);
783:       VecDestroy(&b);
784:       PetscPrintf(PETSC_COMM_WORLD, "Au - b = Au + F(0)\n");
785:       VecChop(r, 1.0e-10);
786:       VecView(r, PETSC_VIEWER_STDOUT_WORLD);
787:       VecNorm(r, NORM_2, &res);
788:       PetscPrintf(PETSC_COMM_WORLD, "Linear L_2 Residual: %g\n", res);
789:     }
790:   }

792:   if (user.runType == RUN_FULL) {
793:     PetscViewer viewer;
794:     Vec         uLocal;
795:     const char *name;

797:     PetscViewerCreate(PETSC_COMM_WORLD, &viewer);
798:     PetscViewerSetType(viewer, PETSCVIEWERVTK);
799:     PetscViewerSetFormat(viewer, PETSC_VIEWER_ASCII_VTK);
800:     PetscViewerFileSetName(viewer, "ex62_sol.vtk");

802:     DMGetLocalVector(dm, &uLocal);
803:     PetscObjectGetName((PetscObject) u, &name);
804:     PetscObjectSetName((PetscObject) uLocal, name);
805:     DMGlobalToLocalBegin(dm, u, INSERT_VALUES, uLocal);
806:     DMGlobalToLocalEnd(dm, u, INSERT_VALUES, uLocal);
807:     VecView(uLocal, viewer);
808:     DMRestoreLocalVector(dm, &uLocal);

810:     PetscViewerDestroy(&viewer);
811:   }

813:   PetscFree(user.exactFuncs);
814:   DestroyElement(&user);
815:   MatNullSpaceDestroy(&nullSpace);
816:   if (user.jacobianMF) {
817:     VecDestroy(&userJ.u);
818:   }
819:   if (A != J) {
820:     MatDestroy(&A);
821:   }
822:   MatDestroy(&J);
823:   VecDestroy(&u);
824:   VecDestroy(&r);
825:   SNESDestroy(&snes);
826:   DMDestroy(&dm);
827:   PetscFinalize();
828:   return 0;
829: }