Actual source code: ex15.c

petsc-3.8.4 2018-03-24
Report Typos and Errors
  1: static const char help[] = "p-Bratu nonlinear PDE in 2d.\n\
  2: We solve the  p-Laplacian (nonlinear diffusion) combined with\n\
  3: the Bratu (solid fuel ignition) nonlinearity in a 2D rectangular\n\
  4: domain, using distributed arrays (DAs) to partition the parallel grid.\n\
  5: The command line options include:\n\
  6:   -p <2>: `p' in p-Laplacian term\n\
  7:   -epsilon <1e-05>: Strain-regularization in p-Laplacian\n\
  8:   -lambda <6>: Bratu parameter\n\
  9:   -blocks <bx,by>: number of coefficient interfaces in x and y direction\n\
 10:   -kappa <1e-3>: diffusivity in odd regions\n\
 11: \n";


The $p$-Bratu problem is a combination of the $p$-Laplacian (nonlinear diffusion) and the Brutu solid fuel ignition problem.
This problem is modeled by the partial differential equation

\begin{equation*}
-\nabla\cdot (\eta \nabla u) - \lambda \exp(u) = 0
\end{equation*}

on $\Omega = (-1,1)^2$ with closure

\begin{align*}
\eta(\gamma) &= (\epsilon^2 + \gamma)^{(p-2)/2} & \gamma &= \frac 1 2 |\nabla u|^2
\end{align*}

and boundary conditions $u = 0$ for $(x,y) \in \partial \Omega$

A 9-point finite difference stencil is used to discretize
the boundary value problem to obtain a nonlinear system of equations.
This would be a 5-point stencil if not for the $p$-Laplacian's nonlinearity.
 35: /*
 36:       mpiexec -n 2 ./ex15 -snes_monitor -ksp_monitor log_summary
 37: */

 39: /*
 40:    Include "petscdmda.h" so that we can use distributed arrays (DMDAs).
 41:    Include "petscsnes.h" so that we can use SNES solvers.  Note that this
 42:    file automatically includes:
 43:      petsc.h       - base PETSc routines   petscvec.h - vectors
 44:      petscsys.h    - system routines       petscmat.h - matrices
 45:      petscis.h     - index sets            petscksp.h - Krylov subspace methods
 46:      petscviewer.h - viewers               petscpc.h  - preconditioners
 47:      petscksp.h   - linear solvers
 48: */
 49:  #include <petscdm.h>
 50:  #include <petscdmda.h>
 51:  #include <petscsnes.h>

 53: /* These functions _should_ be internal, but currently have a reverse dependency so cannot be set with
 54:  * DMDASNESSetPicardLocal.  This hack needs to be fixed in PETSc. */
 55: PETSC_EXTERN PetscErrorCode SNESPicardComputeFunction(SNES,Vec,Vec,void*);
 56: PETSC_EXTERN PetscErrorCode SNESPicardComputeJacobian(SNES,Vec,Mat,Mat,void*);

 58: typedef enum {JAC_BRATU,JAC_PICARD,JAC_STAR,JAC_NEWTON} JacType;
 59: static const char *const JacTypes[] = {"BRATU","PICARD","STAR","NEWTON","JacType","JAC_",0};

 61: /*
 62:    User-defined application context - contains data needed by the
 63:    application-provided call-back routines, FormJacobianLocal() and
 64:    FormFunctionLocal().
 65: */
 66: typedef struct {
 67:   PetscReal   lambda;         /* Bratu parameter */
 68:   PetscReal   p;              /* Exponent in p-Laplacian */
 69:   PetscReal   epsilon;        /* Regularization */
 70:   PetscReal   source;         /* Source term */
 71:   JacType     jtype;          /* What type of Jacobian to assemble */
 72:   PetscBool   picard;
 73:   PetscInt    blocks[2];
 74:   PetscReal   kappa;
 75:   PetscInt    initial;        /* initial conditions type */
 76: } AppCtx;

 78: /*
 79:    User-defined routines
 80: */
 81: static PetscErrorCode FormRHS(AppCtx*,DM,Vec);
 82: static PetscErrorCode FormInitialGuess(AppCtx*,DM,Vec);
 83: static PetscErrorCode FormFunctionLocal(DMDALocalInfo*,PetscScalar**,PetscScalar**,AppCtx*);
 84: static PetscErrorCode FormFunctionPicardLocal(DMDALocalInfo*,PetscScalar**,PetscScalar**,AppCtx*);
 85: static PetscErrorCode FormJacobianLocal(DMDALocalInfo*,PetscScalar**,Mat,Mat,AppCtx*);
 86: static PetscErrorCode NonlinearGS(SNES,Vec,Vec,void*);

 88: typedef struct _n_PreCheck *PreCheck;
 89: struct _n_PreCheck {
 90:   MPI_Comm    comm;
 91:   PetscReal   angle;
 92:   Vec         Ylast;
 93:   PetscViewer monitor;
 94: };
 95: PetscErrorCode PreCheckCreate(MPI_Comm,PreCheck*);
 96: PetscErrorCode PreCheckDestroy(PreCheck*);
 97: PetscErrorCode PreCheckFunction(SNESLineSearch,Vec,Vec,PetscBool*,void*);
 98: PetscErrorCode PreCheckSetFromOptions(PreCheck);

100: int main(int argc,char **argv)
101: {
102:   SNES                snes;                    /* nonlinear solver */
103:   Vec                 x,r,b;                   /* solution, residual, rhs vectors */
104:   Mat                 A,B;                     /* Jacobian and preconditioning matrices */
105:   AppCtx              user;                    /* user-defined work context */
106:   PetscInt            its;                     /* iterations for convergence */
107:   SNESConvergedReason reason;                  /* Check convergence */
108:   PetscBool           alloc_star;              /* Only allocate for the STAR stencil  */
109:   PetscBool           write_output;
110:   char                filename[PETSC_MAX_PATH_LEN] = "ex15.vts";
111:   PetscReal           bratu_lambda_max             = 6.81,bratu_lambda_min = 0.;
112:   DM                  da,dastar;               /* distributed array data structure */
113:   PreCheck            precheck = NULL;    /* precheck context for version in this file */
114:   PetscInt            use_precheck;      /* 0=none, 1=version in this file, 2=SNES-provided version */
115:   PetscReal           precheck_angle;    /* When manually setting the SNES-provided precheck function */
116:   PetscErrorCode      ierr;
117:   SNESLineSearch      linesearch;

119:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
120:      Initialize program
121:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

123:   PetscInitialize(&argc,&argv,(char*)0,help);if (ierr) return ierr;

125:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
126:      Initialize problem parameters
127:   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
128:   user.lambda    = 0.0; user.p = 2.0; user.epsilon = 1e-5; user.source = 0.1; user.jtype = JAC_NEWTON;user.initial=-1;
129:   user.blocks[0] = 1; user.blocks[1] = 1; user.kappa = 1e-3;
130:   alloc_star     = PETSC_FALSE;
131:   use_precheck   = 0; precheck_angle = 10.;
132:   user.picard    = PETSC_FALSE;
133:   PetscOptionsBegin(PETSC_COMM_WORLD,NULL,"p-Bratu options",__FILE__);
134:   {
135:     PetscInt two=2;
136:     PetscOptionsReal("-lambda","Bratu parameter","",user.lambda,&user.lambda,NULL);
137:     PetscOptionsReal("-p","Exponent `p' in p-Laplacian","",user.p,&user.p,NULL);
138:     PetscOptionsReal("-epsilon","Strain-regularization in p-Laplacian","",user.epsilon,&user.epsilon,NULL);
139:     PetscOptionsReal("-source","Constant source term","",user.source,&user.source,NULL);
140:     PetscOptionsEnum("-jtype","Jacobian approximation to assemble","",JacTypes,(PetscEnum)user.jtype,(PetscEnum*)&user.jtype,NULL);
141:     PetscOptionsName("-picard","Solve with defect-correction Picard iteration","",&user.picard);
142:     if (user.picard) {user.jtype = JAC_PICARD; user.p = 3;}
143:     PetscOptionsBool("-alloc_star","Allocate for STAR stencil (5-point)","",alloc_star,&alloc_star,NULL);
144:     PetscOptionsInt("-precheck","Use a pre-check correction intended for use with Picard iteration 1=this version, 2=library","",use_precheck,&use_precheck,NULL);
145:     PetscOptionsInt("-initial","Initial conditions type (-1: default, 0: zero-valued, 1: peaked guess)","",user.initial,&user.initial,NULL);
146:     if (use_precheck == 2) {    /* Using library version, get the angle */
147:       PetscOptionsReal("-precheck_angle","Angle in degrees between successive search directions necessary to activate step correction","",precheck_angle,&precheck_angle,NULL);
148:     }
149:     PetscOptionsIntArray("-blocks","number of coefficient interfaces in x and y direction","",user.blocks,&two,NULL);
150:     PetscOptionsReal("-kappa","diffusivity in odd regions","",user.kappa,&user.kappa,NULL);
151:     PetscOptionsString("-o","Output solution in vts format","",filename,filename,sizeof(filename),&write_output);
152:   }
153:   PetscOptionsEnd();
154:   if (user.lambda > bratu_lambda_max || user.lambda < bratu_lambda_min) {
155:     PetscPrintf(PETSC_COMM_WORLD,"WARNING: lambda %g out of range for p=2\n",user.lambda);
156:   }

158:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
159:      Create nonlinear solver context
160:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
161:   SNESCreate(PETSC_COMM_WORLD,&snes);

163:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
164:      Create distributed array (DMDA) to manage parallel grid and vectors
165:   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
166:   DMDACreate2d(PETSC_COMM_WORLD,DM_BOUNDARY_NONE,DM_BOUNDARY_NONE,DMDA_STENCIL_BOX,4,4,PETSC_DECIDE,PETSC_DECIDE,1,1,NULL,NULL,&da);
167:   DMSetFromOptions(da);
168:   DMSetUp(da);
169:   DMDACreate2d(PETSC_COMM_WORLD,DM_BOUNDARY_NONE,DM_BOUNDARY_NONE,DMDA_STENCIL_STAR,4,4,PETSC_DECIDE,PETSC_DECIDE,1,1,NULL,NULL,&dastar);
170:   DMSetFromOptions(dastar);
171:   DMSetUp(dastar);

173:   /*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
174:      Extract global vectors from DM; then duplicate for remaining
175:      vectors that are the same types
176:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
177:   DMCreateGlobalVector(da,&x);
178:   VecDuplicate(x,&r);
179:   VecDuplicate(x,&b);

181:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
182:      Create matrix data structure; set Jacobian evaluation routine

184:      Set Jacobian matrix data structure and default Jacobian evaluation
185:      routine. User can override with:
186:      -snes_mf : matrix-free Newton-Krylov method with no preconditioning
187:                 (unless user explicitly sets preconditioner)
188:      -snes_mf_operator : form preconditioning matrix as set by the user,
189:                          but use matrix-free approx for Jacobian-vector
190:                          products within Newton-Krylov method

192:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
193:   /* B can be type of MATAIJ,MATBAIJ or MATSBAIJ */
194:   DMCreateMatrix(alloc_star ? dastar : da,&B);
195:   A    = B;

197:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
198:      Set local function evaluation routine
199:   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
200:   DMSetApplicationContext(da, &user);
201:   SNESSetDM(snes,da);
202:   if (user.picard) {
203:     /*
204:         This is not really right requiring the user to call SNESSetFunction/Jacobian but the DMDASNESSetPicardLocal() cannot access
205:         the SNES to set it
206:     */
207:     DMDASNESSetPicardLocal(da,INSERT_VALUES,(PetscErrorCode (*)(DMDALocalInfo*,void*,void*,void*))FormFunctionPicardLocal,
208:                                   (PetscErrorCode (*)(DMDALocalInfo*,void*,Mat,Mat,void*))FormJacobianLocal,&user);
209:     SNESSetFunction(snes,NULL,SNESPicardComputeFunction,&user);
210:     SNESSetJacobian(snes,NULL,NULL,SNESPicardComputeJacobian,&user);
211:   } else {
212:     DMDASNESSetFunctionLocal(da,INSERT_VALUES,(PetscErrorCode (*)(DMDALocalInfo*,void*,void*,void*))FormFunctionLocal,&user);
213:     DMDASNESSetJacobianLocal(da,(PetscErrorCode (*)(DMDALocalInfo*,void*,Mat,Mat,void*))FormJacobianLocal,&user);
214:   }


217:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
218:      Customize nonlinear solver; set runtime options
219:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
220:   SNESSetFromOptions(snes);
221:   SNESSetNGS(snes,NonlinearGS,&user);
222:   SNESGetLineSearch(snes, &linesearch);
223:   /* Set up the precheck context if requested */
224:   if (use_precheck == 1) {      /* Use the precheck routines in this file */
225:     PreCheckCreate(PETSC_COMM_WORLD,&precheck);
226:     PreCheckSetFromOptions(precheck);
227:     SNESLineSearchSetPreCheck(linesearch,PreCheckFunction,precheck);
228:   } else if (use_precheck == 2) { /* Use the version provided by the library */
229:     SNESLineSearchSetPreCheck(linesearch,SNESLineSearchPreCheckPicard,&precheck_angle);
230:   }

232:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
233:      Evaluate initial guess
234:      Note: The user should initialize the vector, x, with the initial guess
235:      for the nonlinear solver prior to calling SNESSolve().  In particular,
236:      to employ an initial guess of zero, the user should explicitly set
237:      this vector to zero by calling VecSet().
238:   */

240:   FormInitialGuess(&user,da,x);
241:   FormRHS(&user,da,b);

243:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
244:      Solve nonlinear system
245:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
246:   SNESSolve(snes,b,x);
247:   SNESGetIterationNumber(snes,&its);
248:   SNESGetConvergedReason(snes,&reason);

250:   PetscPrintf(PETSC_COMM_WORLD,"%s Number of nonlinear iterations = %D\n",SNESConvergedReasons[reason],its);

252:   if (write_output) {
253:     PetscViewer viewer;
254:     PetscViewerVTKOpen(PETSC_COMM_WORLD,filename,FILE_MODE_WRITE,&viewer);
255:     VecView(x,viewer);
256:     PetscViewerDestroy(&viewer);
257:   }

259:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
260:      Free work space.  All PETSc objects should be destroyed when they
261:      are no longer needed.
262:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

264:   if (A != B) {
265:     MatDestroy(&A);
266:   }
267:   MatDestroy(&B);
268:   VecDestroy(&x);
269:   VecDestroy(&r);
270:   VecDestroy(&b);
271:   SNESDestroy(&snes);
272:   DMDestroy(&da);
273:   DMDestroy(&dastar);
274:   PreCheckDestroy(&precheck);
275:   PetscFinalize();
276:   return ierr;
277: }

279: /* ------------------------------------------------------------------- */
280: /*
281:    FormInitialGuess - Forms initial approximation.

283:    Input Parameters:
284:    user - user-defined application context
285:    X - vector

287:    Output Parameter:
288:    X - vector
289:  */
290: static PetscErrorCode FormInitialGuess(AppCtx *user,DM da,Vec X)
291: {
292:   PetscInt       i,j,Mx,My,xs,ys,xm,ym;
294:   PetscReal      temp1,temp,hx,hy;
295:   PetscScalar    **x;

298:   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);

300:   hx    = 1.0/(PetscReal)(Mx-1);
301:   hy    = 1.0/(PetscReal)(My-1);
302:   temp1 = user->lambda / (user->lambda + 1.);

304:   /*
305:      Get a pointer to vector data.
306:        - For default PETSc vectors, VecGetArray() returns a pointer to
307:          the data array.  Otherwise, the routine is implementation dependent.
308:        - You MUST call VecRestoreArray() when you no longer need access to
309:          the array.
310:   */
311:   DMDAVecGetArray(da,X,&x);

313:   /*
314:      Get local grid boundaries (for 2-dimensional DA):
315:        xs, ys   - starting grid indices (no ghost points)
316:        xm, ym   - widths of local grid (no ghost points)

318:   */
319:   DMDAGetCorners(da,&xs,&ys,NULL,&xm,&ym,NULL);

321:   /*
322:      Compute initial guess over the locally owned part of the grid
323:   */
324:   for (j=ys; j<ys+ym; j++) {
325:     temp = (PetscReal)(PetscMin(j,My-j-1))*hy;
326:     for (i=xs; i<xs+xm; i++) {
327:       if (i == 0 || j == 0 || i == Mx-1 || j == My-1) {
328:         /* boundary conditions are all zero Dirichlet */
329:         x[j][i] = 0.0;
330:       } else {
331:         if (user->initial == -1) {
332:           if (user->lambda != 0) {
333:             x[j][i] = temp1*PetscSqrtReal(PetscMin((PetscReal)(PetscMin(i,Mx-i-1))*hx,temp));
334:           } else {
335:             /* The solution above is an exact solution for lambda=0, this avoids "accidentally" starting
336:              * with an exact solution. */
337:             const PetscReal
338:               xx = 2*(PetscReal)i/(Mx-1) - 1,
339:               yy = 2*(PetscReal)j/(My-1) - 1;
340:             x[j][i] = (1 - xx*xx) * (1-yy*yy) * xx * yy;
341:           }
342:         } else if (user->initial == 0) {
343:           x[j][i] = 0.;
344:         } else if (user->initial == 1) {
345:           const PetscReal
346:             xx = 2*(PetscReal)i/(Mx-1) - 1,
347:             yy = 2*(PetscReal)j/(My-1) - 1;
348:           x[j][i] = (1 - xx*xx) * (1-yy*yy) * xx * yy;
349:         } else {
350:           if (user->lambda != 0) {
351:             x[j][i] = temp1*PetscSqrtReal(PetscMin((PetscReal)(PetscMin(i,Mx-i-1))*hx,temp));
352:           } else {
353:             x[j][i] = 0.5*PetscSqrtReal(PetscMin((PetscReal)(PetscMin(i,Mx-i-1))*hx,temp));
354:           }
355:         }
356:       }
357:     }
358:   }
359:   /*
360:      Restore vector
361:   */
362:   DMDAVecRestoreArray(da,X,&x);
363:   return(0);
364: }

366: /*
367:    FormRHS - Forms constant RHS for the problem.

369:    Input Parameters:
370:    user - user-defined application context
371:    B - RHS vector

373:    Output Parameter:
374:    B - vector
375:  */
376: static PetscErrorCode FormRHS(AppCtx *user,DM da,Vec B)
377: {
378:   PetscInt       i,j,Mx,My,xs,ys,xm,ym;
380:   PetscReal      hx,hy;
381:   PetscScalar    **b;

384:   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);

386:   hx    = 1.0/(PetscReal)(Mx-1);
387:   hy    = 1.0/(PetscReal)(My-1);
388:   DMDAVecGetArray(da,B,&b);
389:   DMDAGetCorners(da,&xs,&ys,NULL,&xm,&ym,NULL);
390:   for (j=ys; j<ys+ym; j++) {
391:     for (i=xs; i<xs+xm; i++) {
392:       if (i == 0 || j == 0 || i == Mx-1 || j == My-1) {
393:         b[j][i] = 0.0;
394:       } else {
395:         b[j][i] = hx*hy*user->source;
396:       }
397:     }
398:   }
399:   DMDAVecRestoreArray(da,B,&b);
400:   return(0);
401: }

403: PETSC_STATIC_INLINE PetscReal kappa(const AppCtx *ctx,PetscReal x,PetscReal y)
404: {
405:   return (((PetscInt)(x*ctx->blocks[0])) + ((PetscInt)(y*ctx->blocks[1]))) % 2 ? ctx->kappa : 1.0;
406: }
407: /* p-Laplacian diffusivity */
408: PETSC_STATIC_INLINE PetscScalar eta(const AppCtx *ctx,PetscReal x,PetscReal y,PetscScalar ux,PetscScalar uy)
409: {
410:   return kappa(ctx,x,y) * PetscPowScalar(PetscSqr(ctx->epsilon)+0.5*(ux*ux + uy*uy),0.5*(ctx->p-2.));
411: }
412: PETSC_STATIC_INLINE PetscScalar deta(const AppCtx *ctx,PetscReal x,PetscReal y,PetscScalar ux,PetscScalar uy)
413: {
414:   return (ctx->p == 2)
415:          ? 0
416:          : kappa(ctx,x,y)*PetscPowScalar(PetscSqr(ctx->epsilon)+0.5*(ux*ux + uy*uy),0.5*(ctx->p-4)) * 0.5 * (ctx->p-2.);
417: }


420: /* ------------------------------------------------------------------- */
421: /*
422:    FormFunctionLocal - Evaluates nonlinear function, F(x).
423:  */
424: static PetscErrorCode FormFunctionLocal(DMDALocalInfo *info,PetscScalar **x,PetscScalar **f,AppCtx *user)
425: {
426:   PetscReal      hx,hy,dhx,dhy,sc;
427:   PetscInt       i,j;
428:   PetscScalar    eu;


433:   hx     = 1.0/(PetscReal)(info->mx-1);
434:   hy     = 1.0/(PetscReal)(info->my-1);
435:   sc     = hx*hy*user->lambda;
436:   dhx    = 1/hx;
437:   dhy    = 1/hy;
438:   /*
439:      Compute function over the locally owned part of the grid
440:   */
441:   for (j=info->ys; j<info->ys+info->ym; j++) {
442:     for (i=info->xs; i<info->xs+info->xm; i++) {
443:       PetscReal xx = i*hx,yy = j*hy;
444:       if (i == 0 || j == 0 || i == info->mx-1 || j == info->my-1) {
445:         f[j][i] = x[j][i];
446:       } else {
447:         const PetscScalar
448:           u    = x[j][i],
449:           ux_E = dhx*(x[j][i+1]-x[j][i]),
450:           uy_E = 0.25*dhy*(x[j+1][i]+x[j+1][i+1]-x[j-1][i]-x[j-1][i+1]),
451:           ux_W = dhx*(x[j][i]-x[j][i-1]),
452:           uy_W = 0.25*dhy*(x[j+1][i-1]+x[j+1][i]-x[j-1][i-1]-x[j-1][i]),
453:           ux_N = 0.25*dhx*(x[j][i+1]+x[j+1][i+1]-x[j][i-1]-x[j+1][i-1]),
454:           uy_N = dhy*(x[j+1][i]-x[j][i]),
455:           ux_S = 0.25*dhx*(x[j-1][i+1]+x[j][i+1]-x[j-1][i-1]-x[j][i-1]),
456:           uy_S = dhy*(x[j][i]-x[j-1][i]),
457:           e_E  = eta(user,xx,yy,ux_E,uy_E),
458:           e_W  = eta(user,xx,yy,ux_W,uy_W),
459:           e_N  = eta(user,xx,yy,ux_N,uy_N),
460:           e_S  = eta(user,xx,yy,ux_S,uy_S),
461:           uxx  = -hy * (e_E*ux_E - e_W*ux_W),
462:           uyy  = -hx * (e_N*uy_N - e_S*uy_S);
463:         if (sc) eu = PetscExpScalar(u);
464:         else    eu = 0.;
465:         /** For p=2, these terms decay to:
466:         * uxx = (2.0*u - x[j][i-1] - x[j][i+1])*hydhx
467:         * uyy = (2.0*u - x[j-1][i] - x[j+1][i])*hxdhy
468:         **/
469:         f[j][i] = uxx + uyy - sc*eu;
470:       }
471:     }
472:   }
473:   PetscLogFlops(info->xm*info->ym*(72.0));
474:   return(0);
475: }

477: /*
478:     This is the opposite sign of the part of FormFunctionLocal that excludes the A(x) x part of the operation,
479:     that is FormFunction applies A(x) x - b(x) while this applies b(x) because for Picard we think of it as solving A(x) x = b(x)

481: */
482: static PetscErrorCode FormFunctionPicardLocal(DMDALocalInfo *info,PetscScalar **x,PetscScalar **f,AppCtx *user)
483: {
484:   PetscReal hx,hy,sc;
485:   PetscInt  i,j;

489:   hx     = 1.0/(PetscReal)(info->mx-1);
490:   hy     = 1.0/(PetscReal)(info->my-1);
491:   sc     = hx*hy*user->lambda;
492:   /*
493:      Compute function over the locally owned part of the grid
494:   */
495:   for (j=info->ys; j<info->ys+info->ym; j++) {
496:     for (i=info->xs; i<info->xs+info->xm; i++) {
497:       if (!(i == 0 || j == 0 || i == info->mx-1 || j == info->my-1)) {
498:         const PetscScalar u = x[j][i];
499:         f[j][i] = sc*PetscExpScalar(u);
500:       }
501:     }
502:   }
503:   PetscLogFlops(info->xm*info->ym*2.0);
504:   return(0);
505: }

507: /*
508:    FormJacobianLocal - Evaluates Jacobian matrix.
509: */
510: static PetscErrorCode FormJacobianLocal(DMDALocalInfo *info,PetscScalar **x,Mat J,Mat B,AppCtx *user)
511: {
513:   PetscInt       i,j;
514:   MatStencil     col[9],row;
515:   PetscScalar    v[9];
516:   PetscReal      hx,hy,hxdhy,hydhx,dhx,dhy,sc;

519:   hx    = 1.0/(PetscReal)(info->mx-1);
520:   hy    = 1.0/(PetscReal)(info->my-1);
521:   sc    = hx*hy*user->lambda;
522:   dhx   = 1/hx;
523:   dhy   = 1/hy;
524:   hxdhy = hx/hy;
525:   hydhx = hy/hx;

527:   /*
528:      Compute entries for the locally owned part of the Jacobian.
529:       - PETSc parallel matrix formats are partitioned by
530:         contiguous chunks of rows across the processors.
531:       - Each processor needs to insert only elements that it owns
532:         locally (but any non-local elements will be sent to the
533:         appropriate processor during matrix assembly).
534:       - Here, we set all entries for a particular row at once.
535:   */
536:   for (j=info->ys; j<info->ys+info->ym; j++) {
537:     for (i=info->xs; i<info->xs+info->xm; i++) {
538:       PetscReal xx = i*hx,yy = j*hy;
539:       row.j = j; row.i = i;
540:       /* boundary points */
541:       if (i == 0 || j == 0 || i == info->mx-1 || j == info->my-1) {
542:         v[0] = 1.0;
543:         MatSetValuesStencil(B,1,&row,1,&row,v,INSERT_VALUES);
544:       } else {
545:         /* interior grid points */
546:         const PetscScalar
547:           ux_E     = dhx*(x[j][i+1]-x[j][i]),
548:           uy_E     = 0.25*dhy*(x[j+1][i]+x[j+1][i+1]-x[j-1][i]-x[j-1][i+1]),
549:           ux_W     = dhx*(x[j][i]-x[j][i-1]),
550:           uy_W     = 0.25*dhy*(x[j+1][i-1]+x[j+1][i]-x[j-1][i-1]-x[j-1][i]),
551:           ux_N     = 0.25*dhx*(x[j][i+1]+x[j+1][i+1]-x[j][i-1]-x[j+1][i-1]),
552:           uy_N     = dhy*(x[j+1][i]-x[j][i]),
553:           ux_S     = 0.25*dhx*(x[j-1][i+1]+x[j][i+1]-x[j-1][i-1]-x[j][i-1]),
554:           uy_S     = dhy*(x[j][i]-x[j-1][i]),
555:           u        = x[j][i],
556:           e_E      = eta(user,xx,yy,ux_E,uy_E),
557:           e_W      = eta(user,xx,yy,ux_W,uy_W),
558:           e_N      = eta(user,xx,yy,ux_N,uy_N),
559:           e_S      = eta(user,xx,yy,ux_S,uy_S),
560:           de_E     = deta(user,xx,yy,ux_E,uy_E),
561:           de_W     = deta(user,xx,yy,ux_W,uy_W),
562:           de_N     = deta(user,xx,yy,ux_N,uy_N),
563:           de_S     = deta(user,xx,yy,ux_S,uy_S),
564:           skew_E   = de_E*ux_E*uy_E,
565:           skew_W   = de_W*ux_W*uy_W,
566:           skew_N   = de_N*ux_N*uy_N,
567:           skew_S   = de_S*ux_S*uy_S,
568:           cross_EW = 0.25*(skew_E - skew_W),
569:           cross_NS = 0.25*(skew_N - skew_S),
570:           newt_E   = e_E+de_E*PetscSqr(ux_E),
571:           newt_W   = e_W+de_W*PetscSqr(ux_W),
572:           newt_N   = e_N+de_N*PetscSqr(uy_N),
573:           newt_S   = e_S+de_S*PetscSqr(uy_S);
574:         /* interior grid points */
575:         switch (user->jtype) {
576:         case JAC_BRATU:
577:           /* Jacobian from p=2 */
578:           v[0] = -hxdhy;                                           col[0].j = j-1;   col[0].i = i;
579:           v[1] = -hydhx;                                           col[1].j = j;     col[1].i = i-1;
580:           v[2] = 2.0*(hydhx + hxdhy) - sc*PetscExpScalar(u);       col[2].j = row.j; col[2].i = row.i;
581:           v[3] = -hydhx;                                           col[3].j = j;     col[3].i = i+1;
582:           v[4] = -hxdhy;                                           col[4].j = j+1;   col[4].i = i;
583:           MatSetValuesStencil(B,1,&row,5,col,v,INSERT_VALUES);
584:           break;
585:         case JAC_PICARD:
586:           /* Jacobian arising from Picard linearization */
587:           v[0] = -hxdhy*e_S;                                           col[0].j = j-1;   col[0].i = i;
588:           v[1] = -hydhx*e_W;                                           col[1].j = j;     col[1].i = i-1;
589:           v[2] = (e_W+e_E)*hydhx + (e_S+e_N)*hxdhy;                    col[2].j = row.j; col[2].i = row.i;
590:           v[3] = -hydhx*e_E;                                           col[3].j = j;     col[3].i = i+1;
591:           v[4] = -hxdhy*e_N;                                           col[4].j = j+1;   col[4].i = i;
592:           MatSetValuesStencil(B,1,&row,5,col,v,INSERT_VALUES);
593:           break;
594:         case JAC_STAR:
595:           /* Full Jacobian, but only a star stencil */
596:           col[0].j = j-1; col[0].i = i;
597:           col[1].j = j;   col[1].i = i-1;
598:           col[2].j = j;   col[2].i = i;
599:           col[3].j = j;   col[3].i = i+1;
600:           col[4].j = j+1; col[4].i = i;
601:           v[0]     = -hxdhy*newt_S + cross_EW;
602:           v[1]     = -hydhx*newt_W + cross_NS;
603:           v[2]     = hxdhy*(newt_N + newt_S) + hydhx*(newt_E + newt_W) - sc*PetscExpScalar(u);
604:           v[3]     = -hydhx*newt_E - cross_NS;
605:           v[4]     = -hxdhy*newt_N - cross_EW;
606:           MatSetValuesStencil(B,1,&row,5,col,v,INSERT_VALUES);
607:           break;
608:         case JAC_NEWTON:
609:           /** The Jacobian is
610:           *
611:           * -div [ eta (grad u) + deta (grad u0 . grad u) grad u0 ] - (eE u0) u
612:           *
613:           **/
614:           col[0].j = j-1; col[0].i = i-1;
615:           col[1].j = j-1; col[1].i = i;
616:           col[2].j = j-1; col[2].i = i+1;
617:           col[3].j = j;   col[3].i = i-1;
618:           col[4].j = j;   col[4].i = i;
619:           col[5].j = j;   col[5].i = i+1;
620:           col[6].j = j+1; col[6].i = i-1;
621:           col[7].j = j+1; col[7].i = i;
622:           col[8].j = j+1; col[8].i = i+1;
623:           v[0]     = -0.25*(skew_S + skew_W);
624:           v[1]     = -hxdhy*newt_S + cross_EW;
625:           v[2]     =  0.25*(skew_S + skew_E);
626:           v[3]     = -hydhx*newt_W + cross_NS;
627:           v[4]     = hxdhy*(newt_N + newt_S) + hydhx*(newt_E + newt_W) - sc*PetscExpScalar(u);
628:           v[5]     = -hydhx*newt_E - cross_NS;
629:           v[6]     =  0.25*(skew_N + skew_W);
630:           v[7]     = -hxdhy*newt_N - cross_EW;
631:           v[8]     = -0.25*(skew_N + skew_E);
632:           MatSetValuesStencil(B,1,&row,9,col,v,INSERT_VALUES);
633:           break;
634:         default:
635:           SETERRQ1(PetscObjectComm((PetscObject)info->da),PETSC_ERR_SUP,"Jacobian type %d not implemented",user->jtype);
636:         }
637:       }
638:     }
639:   }

641:   /*
642:      Assemble matrix, using the 2-step process:
643:        MatAssemblyBegin(), MatAssemblyEnd().
644:   */
645:   MatAssemblyBegin(B,MAT_FINAL_ASSEMBLY);
646:   MatAssemblyEnd(B,MAT_FINAL_ASSEMBLY);

648:   if (J != B) {
649:     MatAssemblyBegin(J,MAT_FINAL_ASSEMBLY);
650:     MatAssemblyEnd(J,MAT_FINAL_ASSEMBLY);
651:   }

653:   /*
654:      Tell the matrix we will never add a new nonzero location to the
655:      matrix. If we do, it will generate an error.
656:   */
657:   if (user->jtype == JAC_NEWTON) {
658:     PetscLogFlops(info->xm*info->ym*(131.0));
659:   }
660:   MatSetOption(B,MAT_NEW_NONZERO_LOCATION_ERR,PETSC_TRUE);
661:   return(0);
662: }

664: /***********************************************************
665:  * PreCheck implementation
666:  ***********************************************************/
667: PetscErrorCode PreCheckSetFromOptions(PreCheck precheck)
668: {
670:   PetscBool      flg;

673:   PetscOptionsBegin(precheck->comm,NULL,"PreCheck Options","none");
674:   PetscOptionsReal("-precheck_angle","Angle in degrees between successive search directions necessary to activate step correction","",precheck->angle,&precheck->angle,NULL);
675:   flg  = PETSC_FALSE;
676:   PetscOptionsBool("-precheck_monitor","Monitor choices made by precheck routine","",flg,&flg,NULL);
677:   if (flg) {
678:     PetscViewerASCIIOpen(precheck->comm,"stdout",&precheck->monitor);
679:   }
680:   PetscOptionsEnd();
681:   return(0);
682: }

684: /*
685:   Compare the direction of the current and previous step, modify the current step accordingly
686: */
687: PetscErrorCode PreCheckFunction(SNESLineSearch linesearch,Vec X,Vec Y,PetscBool *changed, void *ctx)
688: {
690:   PreCheck       precheck;
691:   Vec            Ylast;
692:   PetscScalar    dot;
693:   PetscInt       iter;
694:   PetscReal      ynorm,ylastnorm,theta,angle_radians;
695:   SNES           snes;

698:   SNESLineSearchGetSNES(linesearch, &snes);
699:   precheck = (PreCheck)ctx;
700:   if (!precheck->Ylast) {VecDuplicate(Y,&precheck->Ylast);}
701:   Ylast = precheck->Ylast;
702:   SNESGetIterationNumber(snes,&iter);
703:   if (iter < 1) {
704:     VecCopy(Y,Ylast);
705:     *changed = PETSC_FALSE;
706:     return(0);
707:   }

709:   VecDot(Y,Ylast,&dot);
710:   VecNorm(Y,NORM_2,&ynorm);
711:   VecNorm(Ylast,NORM_2,&ylastnorm);
712:   /* Compute the angle between the vectors Y and Ylast, clip to keep inside the domain of acos() */
713:   theta         = PetscAcosReal((PetscReal)PetscClipInterval(PetscAbsScalar(dot) / (ynorm * ylastnorm),-1.0,1.0));
714:   angle_radians = precheck->angle * PETSC_PI / 180.;
715:   if (PetscAbsReal(theta) < angle_radians || PetscAbsReal(theta - PETSC_PI) < angle_radians) {
716:     /* Modify the step Y */
717:     PetscReal alpha,ydiffnorm;
718:     VecAXPY(Ylast,-1.0,Y);
719:     VecNorm(Ylast,NORM_2,&ydiffnorm);
720:     alpha = ylastnorm / ydiffnorm;
721:     VecCopy(Y,Ylast);
722:     VecScale(Y,alpha);
723:     if (precheck->monitor) {
724:       PetscViewerASCIIPrintf(precheck->monitor,"Angle %E degrees less than threshold %g, corrected step by alpha=%g\n",(double)(theta*180./PETSC_PI),(double)precheck->angle,(double)alpha);
725:     }
726:   } else {
727:     VecCopy(Y,Ylast);
728:     *changed = PETSC_FALSE;
729:     if (precheck->monitor) {
730:       PetscViewerASCIIPrintf(precheck->monitor,"Angle %E degrees exceeds threshold %g, no correction applied\n",(double)(theta*180./PETSC_PI),(double)precheck->angle);
731:     }
732:   }
733:   return(0);
734: }

736: PetscErrorCode PreCheckDestroy(PreCheck *precheck)
737: {

741:   if (!*precheck) return(0);
742:   VecDestroy(&(*precheck)->Ylast);
743:   PetscViewerDestroy(&(*precheck)->monitor);
744:   PetscFree(*precheck);
745:   return(0);
746: }

748: PetscErrorCode PreCheckCreate(MPI_Comm comm,PreCheck *precheck)
749: {

753:   PetscNew(precheck);

755:   (*precheck)->comm  = comm;
756:   (*precheck)->angle = 10.;     /* only active if angle is less than 10 degrees */
757:   return(0);
758: }

760: /*
761:       Applies some sweeps on nonlinear Gauss-Seidel on each process

763:  */
764: PetscErrorCode NonlinearGS(SNES snes,Vec X, Vec B, void *ctx)
765: {
766:   PetscInt       i,j,k,xs,ys,xm,ym,its,tot_its,sweeps,l,m;
768:   PetscReal      hx,hy,hxdhy,hydhx,dhx,dhy,sc;
769:   PetscScalar    **x,**b,bij,F,F0=0,J,y,u,eu;
770:   PetscReal      atol,rtol,stol;
771:   DM             da;
772:   AppCtx         *user = (AppCtx*)ctx;
773:   Vec            localX,localB;
774:   DMDALocalInfo  info;

777:   SNESGetDM(snes,&da);
778:   DMDAGetLocalInfo(da,&info);

780:   hx     = 1.0/(PetscReal)(info.mx-1);
781:   hy     = 1.0/(PetscReal)(info.my-1);
782:   sc     = hx*hy*user->lambda;
783:   dhx    = 1/hx;
784:   dhy    = 1/hy;
785:   hxdhy  = hx/hy;
786:   hydhx  = hy/hx;

788:   tot_its = 0;
789:   SNESNGSGetSweeps(snes,&sweeps);
790:   SNESNGSGetTolerances(snes,&atol,&rtol,&stol,&its);
791:   DMGetLocalVector(da,&localX);
792:   if (B) {
793:     DMGetLocalVector(da,&localB);
794:   }
795:   if (B) {
796:     DMGlobalToLocalBegin(da,B,INSERT_VALUES,localB);
797:     DMGlobalToLocalEnd(da,B,INSERT_VALUES,localB);
798:   }
799:   if (B) DMDAVecGetArrayRead(da,localB,&b);
800:   DMGlobalToLocalBegin(da,X,INSERT_VALUES,localX);
801:   DMGlobalToLocalEnd(da,X,INSERT_VALUES,localX);
802:   DMDAVecGetArray(da,localX,&x);
803:   for (l=0; l<sweeps; l++) {
804:     /*
805:      Get local grid boundaries (for 2-dimensional DMDA):
806:      xs, ys   - starting grid indices (no ghost points)
807:      xm, ym   - widths of local grid (no ghost points)
808:      */
809:     DMDAGetCorners(da,&xs,&ys,NULL,&xm,&ym,NULL);
810:     for (m=0; m<2; m++) {
811:       for (j=ys; j<ys+ym; j++) {
812:         for (i=xs+(m+j)%2; i<xs+xm; i+=2) {
813:           PetscReal xx = i*hx,yy = j*hy;
814:           if (B) bij = b[j][i];
815:           else   bij = 0.;

817:           if (i == 0 || j == 0 || i == info.mx-1 || j == info.my-1) {
818:             /* boundary conditions are all zero Dirichlet */
819:             x[j][i] = 0.0 + bij;
820:           } else {
821:             const PetscScalar
822:               u_E = x[j][i+1],
823:               u_W = x[j][i-1],
824:               u_N = x[j+1][i],
825:               u_S = x[j-1][i];
826:             const PetscScalar
827:               uy_E   = 0.25*dhy*(x[j+1][i]+x[j+1][i+1]-x[j-1][i]-x[j-1][i+1]),
828:               uy_W   = 0.25*dhy*(x[j+1][i-1]+x[j+1][i]-x[j-1][i-1]-x[j-1][i]),
829:               ux_N   = 0.25*dhx*(x[j][i+1]+x[j+1][i+1]-x[j][i-1]-x[j+1][i-1]),
830:               ux_S   = 0.25*dhx*(x[j-1][i+1]+x[j][i+1]-x[j-1][i-1]-x[j][i-1]);
831:             u = x[j][i];
832:             for (k=0; k<its; k++) {
833:               const PetscScalar
834:                 ux_E   = dhx*(u_E-u),
835:                 ux_W   = dhx*(u-u_W),
836:                 uy_N   = dhy*(u_N-u),
837:                 uy_S   = dhy*(u-u_S),
838:                 e_E    = eta(user,xx,yy,ux_E,uy_E),
839:                 e_W    = eta(user,xx,yy,ux_W,uy_W),
840:                 e_N    = eta(user,xx,yy,ux_N,uy_N),
841:                 e_S    = eta(user,xx,yy,ux_S,uy_S),
842:                 de_E   = deta(user,xx,yy,ux_E,uy_E),
843:                 de_W   = deta(user,xx,yy,ux_W,uy_W),
844:                 de_N   = deta(user,xx,yy,ux_N,uy_N),
845:                 de_S   = deta(user,xx,yy,ux_S,uy_S),
846:                 newt_E = e_E+de_E*PetscSqr(ux_E),
847:                 newt_W = e_W+de_W*PetscSqr(ux_W),
848:                 newt_N = e_N+de_N*PetscSqr(uy_N),
849:                 newt_S = e_S+de_S*PetscSqr(uy_S),
850:                 uxx    = -hy * (e_E*ux_E - e_W*ux_W),
851:                 uyy    = -hx * (e_N*uy_N - e_S*uy_S);

853:               if (sc) eu = PetscExpScalar(u);
854:               else    eu = 0;

856:               F = uxx + uyy - sc*eu - bij;
857:               if (k == 0) F0 = F;
858:               J  = hxdhy*(newt_N + newt_S) + hydhx*(newt_E + newt_W) - sc*eu;
859:               y  = F/J;
860:               u -= y;
861:               tot_its++;
862:               if (atol > PetscAbsReal(PetscRealPart(F)) ||
863:                   rtol*PetscAbsReal(PetscRealPart(F0)) > PetscAbsReal(PetscRealPart(F)) ||
864:                   stol*PetscAbsReal(PetscRealPart(u)) > PetscAbsReal(PetscRealPart(y))) {
865:                 break;
866:               }
867:             }
868:             x[j][i] = u;
869:           }
870:         }
871:       }
872:     }
873:     /*
874: x     Restore vector
875:      */
876:   }
877:   DMDAVecRestoreArray(da,localX,&x);
878:   DMLocalToGlobalBegin(da,localX,INSERT_VALUES,X);
879:   DMLocalToGlobalEnd(da,localX,INSERT_VALUES,X);
880:   PetscLogFlops(tot_its*(118.0));
881:   DMRestoreLocalVector(da,&localX);
882:   if (B) {
883:     DMDAVecRestoreArrayRead(da,localB,&b);
884:     DMRestoreLocalVector(da,&localB);
885:   }
886:   return(0);
887: }