Actual source code: ex15.c petsc-3.7.7 2017-09-25 
   
  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" ;
 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:   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)102:  103:  {
104:    SNES                 snes;                    /* nonlinear solver */ 
105:    Vec                  x,r,b;                   /* solution, residual, rhs vectors */ 
106:    Mat                  A,B;                     /* Jacobian and preconditioning matrices */ 
107:    AppCtx              user;                    /* user-defined work context */ 
108:    PetscInt             its;                     /* iterations for convergence */ 
109:    SNESConvergedReason  reason;                  /* Check convergence */ 
110:    PetscBool            alloc_star;              /* Only allocate for the STAR stencil  */ 
111:    PetscBool            write_output;
112:    char                filename[PETSC_MAX_PATH_LEN] = "ex15.vts" ;
113:    PetscReal            bratu_lambda_max             = 6.81,bratu_lambda_min = 0.;
114:    DM                   da,dastar;               /* distributed array data structure */ 
115:    PreCheck            precheck = NULL;    /* precheck context for version in this file */ 
116:    PetscInt             use_precheck;      /* 0=none, 1=version in this file, 2=SNES -provided version */ 
117:    PetscReal            precheck_angle;    /* When manually setting the SNES -provided precheck function */ 
118:    PetscErrorCode       ierr;
119:    SNESLineSearch       linesearch;
121:    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
122:       Initialize program 
123:       - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
125:    PetscInitialize (&argc,&argv,(char*)0,help);
127:    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
128:       Initialize problem parameters 
129:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
130:    user.lambda    = 0.0; user.p = 2.0; user.epsilon = 1e-5; user.source = 0.1; user.jtype = JAC_NEWTON;user.initial=-1;
131:    user.blocks[0] = 1; user.blocks[1] = 1; user.kappa = 1e-3;
132:    alloc_star     = PETSC_FALSE ;
133:    use_precheck   = 0; precheck_angle = 10.;
134:    user.picard    = PETSC_FALSE ;
135:    PetscOptionsBegin (PETSC_COMM_WORLD ,NULL,"p-Bratu options" ,__FILE__);
136:    {
137:      PetscInt  two=2;
138:      PetscOptionsReal ("-lambda" ,"Bratu parameter" ,"" ,user.lambda,&user.lambda,NULL);
139:      PetscOptionsReal ("-p" ,"Exponent `p' in p-Laplacian" ,"" ,user.p,&user.p,NULL);
140:      PetscOptionsReal ("-epsilon" ,"Strain-regularization in p-Laplacian" ,"" ,user.epsilon,&user.epsilon,NULL);
141:      PetscOptionsReal ("-source" ,"Constant source term" ,"" ,user.source,&user.source,NULL);
142:      PetscOptionsEnum ("-jtype" ,"Jacobian approximation to assemble" ,"" ,JacTypes,(PetscEnum )user.jtype,(PetscEnum *)&user.jtype,NULL);
143:      PetscOptionsName ("-picard" ,"Solve with defect-correction Picard iteration" ,"" ,&user.picard);
144:      if  (user.picard) {user.jtype = JAC_PICARD; user.p = 3;}
145:      PetscOptionsBool ("-alloc_star" ,"Allocate for STAR stencil (5-point)" ,"" ,alloc_star,&alloc_star,NULL);
146:      PetscOptionsInt ("-precheck" ,"Use a pre-check correction intended for use with Picard iteration 1=this version, 2=library" ,"" ,use_precheck,&use_precheck,NULL);
147:      PetscOptionsInt ("-initial" ,"Initial conditions type (-1: default, 0: zero-valued, 1: peaked guess)" ,"" ,user.initial,&user.initial,NULL);
148:      if  (use_precheck == 2) {    /* Using library version, get the angle */ 
149:        PetscOptionsReal ("-precheck_angle" ,"Angle in degrees between successive search directions necessary to activate step correction" ,"" ,precheck_angle,&precheck_angle,NULL);
150:      }
151:      PetscOptionsIntArray ("-blocks" ,"number of coefficient interfaces in x and y direction" ,"" ,user.blocks,&two,NULL);
152:      PetscOptionsReal ("-kappa" ,"diffusivity in odd regions" ,"" ,user.kappa,&user.kappa,NULL);
153:      PetscOptionsString ("-o" ,"Output solution in vts format" ,"" ,filename,filename,sizeof (filename),&write_output);
154:    }
155:    PetscOptionsEnd ();
156:    if  (user.lambda > bratu_lambda_max || user.lambda < bratu_lambda_min) {
157:      PetscPrintf (PETSC_COMM_WORLD ,"WARNING: lambda %g out of range for p=2\n" ,user.lambda);
158:    }
160:    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
161:       Create nonlinear solver context 
162:       - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
163:    SNESCreate (PETSC_COMM_WORLD ,&snes);
165:    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
166:       Create distributed array (DMDA ) to manage parallel grid and vectors 
167:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
168:    DMDACreate2d (PETSC_COMM_WORLD ,DM_BOUNDARY_NONE,DM_BOUNDARY_NONE,DMDA_STENCIL_BOX ,-4,-4,PETSC_DECIDE ,PETSC_DECIDE ,
169:                        1,1,NULL,NULL,&da);
170:    DMDACreate2d (PETSC_COMM_WORLD ,DM_BOUNDARY_NONE,DM_BOUNDARY_NONE,DMDA_STENCIL_STAR ,-4,-4,PETSC_DECIDE ,PETSC_DECIDE ,
171:                        1,1,NULL,NULL,&dastar);
174:    /*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
175:       Extract global vectors from DM ; then duplicate for remaining 
176:       vectors that are the same types 
177:     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
178:    DMCreateGlobalVector (da,&x);
179:    VecDuplicate (x,&r);
180:    VecDuplicate (x,&b);
182:    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
183:       Create matrix data structure; set Jacobian evaluation routine 
185:       Set Jacobian matrix data structure and default Jacobian evaluation 
186:       routine. User can override with: 
187:       -snes_mf : matrix-free Newton-Krylov method with no preconditioning 
188:                  (unless user explicitly sets preconditioner) 
189:       -snes_mf_operator : form preconditioning matrix as set by the user, 
190:                           but use matrix-free approx for Jacobian-vector 
191:                           products within Newton-Krylov method 
193:       - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
194:    /* B can be type of MATAIJ ,MATBAIJ  or MATSBAIJ  */ 
195:    DMCreateMatrix (alloc_star ? dastar : da,&B);
196:    A    = B;
198:    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
199:       Set local function evaluation routine 
200:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
201:    DMSetApplicationContext (da, &user);
202:    SNESSetDM (snes,da);
203:    if  (user.picard) {
204:      /* 
205:          This is not really right requiring the user to call SNESSetFunction /Jacobian but the DMDASNESSetPicardLocal () cannot access 
206:          the SNES  to set it 
207:      */ 
208:      DMDASNESSetPicardLocal (da,INSERT_VALUES ,(PetscErrorCode  (*)(DMDALocalInfo *,void*,void*,void*))FormFunctionPicardLocal,
209:                                    (PetscErrorCode  (*)(DMDALocalInfo *,void*,Mat ,Mat ,void*))FormJacobianLocal,&user);
210:      SNESSetFunction (snes,NULL,SNESPicardComputeFunction,&user);
211:      SNESSetJacobian (snes,NULL,NULL,SNESPicardComputeJacobian,&user);
212:    } else  {
213:      DMDASNESSetFunctionLocal (da,INSERT_VALUES ,(PetscErrorCode  (*)(DMDALocalInfo *,void*,void*,void*))FormFunctionLocal,&user);
214:      DMDASNESSetJacobianLocal (da,(PetscErrorCode  (*)(DMDALocalInfo *,void*,Mat ,Mat ,void*))FormJacobianLocal,&user);
215:    }
218:    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
219:       Customize nonlinear solver; set runtime options 
220:     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
221:    SNESSetFromOptions (snes);
222:    SNESSetNGS (snes,NonlinearGS,&user);
223:    SNESGetLineSearch (snes, &linesearch);
224:    /* Set up the precheck context if requested */ 
225:    if  (use_precheck == 1) {      /* Use the precheck routines in this file */ 
226:      PreCheckCreate(PETSC_COMM_WORLD ,&precheck);
227:      PreCheckSetFromOptions(precheck);
228:      SNESLineSearchSetPreCheck (linesearch,PreCheckFunction,precheck);
229:    } else  if  (use_precheck == 2) { /* Use the version provided by the library */ 
230:      SNESLineSearchSetPreCheck (linesearch,SNESLineSearchPreCheckPicard ,&precheck_angle);
231:    }
233:    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
234:       Evaluate initial guess 
235:       Note: The user should initialize the vector, x, with the initial guess 
236:       for the nonlinear solver prior to calling SNESSolve ().  In particular, 
237:       to employ an initial guess of zero, the user should explicitly set 
238:       this vector to zero by calling VecSet (). 
239:    */ 
241:    FormInitialGuess(&user,da,x);
242:    FormRHS(&user,da,b);
244:    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
245:       Solve nonlinear system 
246:       - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
247:    SNESSolve (snes,b,x);
248:    SNESGetIterationNumber (snes,&its);
249:    SNESGetConvergedReason (snes,&reason);
251:    PetscPrintf (PETSC_COMM_WORLD ,"%s Number of nonlinear iterations = %D\n" ,SNESConvergedReasons[reason],its);
253:    if  (write_output) {
254:      PetscViewer  viewer;
255:      PetscViewerVTKOpen (PETSC_COMM_WORLD ,filename,FILE_MODE_WRITE,&viewer);
256:      VecView (x,viewer);
257:      PetscViewerDestroy (&viewer);
258:    }
260:    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
261:       Free work space.  All PETSc objects should be destroyed when they 
262:       are no longer needed. 
263:     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
265:    if  (A != B) {
266:      MatDestroy (&A);
267:    }
268:    MatDestroy (&B);
269:    VecDestroy (&x);
270:    VecDestroy (&r);
271:    VecDestroy (&b);
272:    SNESDestroy (&snes);
273:    DMDestroy (&da);
274:    DMDestroy (&dastar);
275:    PreCheckDestroy(&precheck);
276:    PetscFinalize ();
277:    return  0;
278:  }
280:  /* ------------------------------------------------------------------- */ 
283:  /* 
284:     FormInitialGuess - Forms initial approximation. 
286:     Input Parameters: 
287:     user - user-defined application context 
288:     X - vector 
290:     Output Parameter: 
291:     X - vector 
292:   */ 
293:  PetscErrorCode  FormInitialGuess(AppCtx *user,DM  da,Vec  X)294:  {
295:    PetscInt        i,j,Mx,My,xs,ys,xm,ym;
297:    PetscReal       temp1,temp,hx,hy;
298:    PetscScalar     **x;
301:    DMDAGetInfo (da,PETSC_IGNORE ,&Mx,&My,PETSC_IGNORE ,PETSC_IGNORE ,PETSC_IGNORE ,
302:                       PETSC_IGNORE ,PETSC_IGNORE ,PETSC_IGNORE ,PETSC_IGNORE ,PETSC_IGNORE ,PETSC_IGNORE ,PETSC_IGNORE );
304:    hx    = 1.0/(PetscReal )(Mx-1);
305:    hy    = 1.0/(PetscReal )(My-1);
306:    temp1 = user->lambda / (user->lambda + 1.);
308:    /* 
309:       Get a pointer to vector data. 
310:         - For default PETSc vectors, VecGetArray () returns a pointer to 
311:           the data array.  Otherwise, the routine is implementation dependent. 
312:         - You MUST call VecRestoreArray () when you no longer need access to 
313:           the array. 
314:    */ 
315:    DMDAVecGetArray (da,X,&x);
317:    /* 
318:       Get local grid boundaries (for 2-dimensional DA): 
319:         xs, ys   - starting grid indices (no ghost points) 
320:         xm, ym   - widths of local grid (no ghost points) 
322:    */ 
323:    DMDAGetCorners (da,&xs,&ys,NULL,&xm,&ym,NULL);
325:    /* 
326:       Compute initial guess over the locally owned part of the grid 
327:    */ 
328:    for  (j=ys; j<ys+ym; j++) {
329:      temp = (PetscReal )(PetscMin (j,My-j-1))*hy;
330:      for  (i=xs; i<xs+xm; i++) {
331:        if  (i == 0 || j == 0 || i == Mx-1 || j == My-1) {
332:          /* boundary conditions are all zero Dirichlet */ 
333:          x[j][i] = 0.0;
334:        } else  {
335:          if  (user->initial == -1) {
336:            if  (user->lambda != 0) {
337:              x[j][i] = temp1*PetscSqrtReal(PetscMin ((PetscReal )(PetscMin (i,Mx-i-1))*hx,temp));
338:            } else  {
339:              /* The solution above is an exact solution for lambda=0, this avoids "accidentally" starting 
340:               * with an exact solution. */ 
341:              const PetscReal 
342:                xx = 2*(PetscReal )i/(Mx-1) - 1,
343:                yy = 2*(PetscReal )j/(My-1) - 1;
344:              x[j][i] = (1 - xx*xx) * (1-yy*yy) * xx * yy;
345:            }
346:          } else  if  (user->initial == 0) {
347:            x[j][i] = 0.;
348:          } else  if  (user->initial == 1) {
349:            const PetscReal 
350:              xx = 2*(PetscReal )i/(Mx-1) - 1,
351:              yy = 2*(PetscReal )j/(My-1) - 1;
352:            x[j][i] = (1 - xx*xx) * (1-yy*yy) * xx * yy;
353:          } else  {
354:            if  (user->lambda != 0) {
355:              x[j][i] = temp1*PetscSqrtReal(PetscMin ((PetscReal )(PetscMin (i,Mx-i-1))*hx,temp));
356:            } else  {
357:              x[j][i] = 0.5*PetscSqrtReal(PetscMin ((PetscReal )(PetscMin (i,Mx-i-1))*hx,temp));
358:            }
359:          }
360:        }
361:      }
362:    }
363:    /* 
364:       Restore vector 
365:    */ 
366:    DMDAVecRestoreArray (da,X,&x);
367:    return (0);
368:  }
372:  /* 
373:     FormRHS - Forms constant RHS for the problem. 
375:     Input Parameters: 
376:     user - user-defined application context 
377:     B - RHS vector 
379:     Output Parameter: 
380:     B - vector 
381:   */ 
382:  PetscErrorCode  FormRHS(AppCtx *user,DM  da,Vec  B)383:  {
384:    PetscInt        i,j,Mx,My,xs,ys,xm,ym;
386:    PetscReal       hx,hy;
387:    PetscScalar     **b;
390:    DMDAGetInfo (da,PETSC_IGNORE ,&Mx,&My,PETSC_IGNORE ,PETSC_IGNORE ,PETSC_IGNORE ,
391:                       PETSC_IGNORE ,PETSC_IGNORE ,PETSC_IGNORE ,PETSC_IGNORE ,PETSC_IGNORE ,PETSC_IGNORE ,PETSC_IGNORE );
393:    hx    = 1.0/(PetscReal )(Mx-1);
394:    hy    = 1.0/(PetscReal )(My-1);
395:    DMDAVecGetArray (da,B,&b);
396:    DMDAGetCorners (da,&xs,&ys,NULL,&xm,&ym,NULL);
397:    for  (j=ys; j<ys+ym; j++) {
398:      for  (i=xs; i<xs+xm; i++) {
399:        if  (i == 0 || j == 0 || i == Mx-1 || j == My-1) {
400:          b[j][i] = 0.0;
401:        } else  {
402:          b[j][i] = hx*hy*user->source;
403:        }
404:      }
405:    }
406:    DMDAVecRestoreArray (da,B,&b);
407:    return (0);
408:  }
410:  PetscReal  kappa(const AppCtx *ctx,PetscReal  x,PetscReal  y)411:  {
412:    return  (((PetscInt )(x*ctx->blocks[0])) + ((PetscInt )(y*ctx->blocks[1]))) % 2 ? ctx->kappa : 1.0;
413:  }
414:  /* p-Laplacian diffusivity */ 
415:  PetscScalar  eta(const AppCtx *ctx,PetscReal  x,PetscReal  y,PetscScalar  ux,PetscScalar  uy)416:  {
417:    return  kappa(ctx,x,y) * PetscPowScalar(PetscSqr (ctx->epsilon)+0.5*(ux*ux + uy*uy),0.5*(ctx->p-2.));
418:  }
419:  PetscScalar  deta(const AppCtx *ctx,PetscReal  x,PetscReal  y,PetscScalar  ux,PetscScalar  uy)420:  {
421:    return  (ctx->p == 2)
422:           ? 0
423:           : kappa(ctx,x,y)*PetscPowScalar(PetscSqr (ctx->epsilon)+0.5*(ux*ux + uy*uy),0.5*(ctx->p-4)) * 0.5 * (ctx->p-2.);
424:  }
427:  /* ------------------------------------------------------------------- */ 
430:  /* 
431:     FormFunctionLocal - Evaluates nonlinear function, F(x). 
432:   */ 
433:  PetscErrorCode  FormFunctionLocal(DMDALocalInfo  *info,PetscScalar  **x,PetscScalar  **f,AppCtx *user)434:  {
435:    PetscReal       hx,hy,dhx,dhy,sc;
436:    PetscInt        i,j;
437:    PetscScalar     eu;
442:    hx     = 1.0/(PetscReal )(info->mx-1);
443:    hy     = 1.0/(PetscReal )(info->my-1);
444:    sc     = hx*hy*user->lambda;
445:    dhx    = 1/hx;
446:    dhy    = 1/hy;
447:    /* 
448:       Compute function over the locally owned part of the grid 
449:    */ 
450:    for  (j=info->ys; j<info->ys+info->ym; j++) {
451:      for  (i=info->xs; i<info->xs+info->xm; i++) {
452:        PetscReal  xx = i*hx,yy = j*hy;
453:        if  (i == 0 || j == 0 || i == info->mx-1 || j == info->my-1) {
454:          f[j][i] = x[j][i];
455:        } else  {
456:          const PetscScalar 
457:            u    = x[j][i],
458:            ux_E = dhx*(x[j][i+1]-x[j][i]),
459:            uy_E = 0.25*dhy*(x[j+1][i]+x[j+1][i+1]-x[j-1][i]-x[j-1][i+1]),
460:            ux_W = dhx*(x[j][i]-x[j][i-1]),
461:            uy_W = 0.25*dhy*(x[j+1][i-1]+x[j+1][i]-x[j-1][i-1]-x[j-1][i]),
462:            ux_N = 0.25*dhx*(x[j][i+1]+x[j+1][i+1]-x[j][i-1]-x[j+1][i-1]),
463:            uy_N = dhy*(x[j+1][i]-x[j][i]),
464:            ux_S = 0.25*dhx*(x[j-1][i+1]+x[j][i+1]-x[j-1][i-1]-x[j][i-1]),
465:            uy_S = dhy*(x[j][i]-x[j-1][i]),
466:            e_E  = eta(user,xx,yy,ux_E,uy_E),
467:            e_W  = eta(user,xx,yy,ux_W,uy_W),
468:            e_N  = eta(user,xx,yy,ux_N,uy_N),
469:            e_S  = eta(user,xx,yy,ux_S,uy_S),
470:            uxx  = -hy * (e_E*ux_E - e_W*ux_W),
471:            uyy  = -hx * (e_N*uy_N - e_S*uy_S);
472:          if  (sc) eu = PetscExpScalar(u);
473:          else     eu = 0.;
474:          /** For p=2, these terms decay to: 
475:          * uxx = (2.0*u - x[j][i-1] - x[j][i+1])*hydhx 
476:          * uyy = (2.0*u - x[j-1][i] - x[j+1][i])*hxdhy 
477:          **/ 
478:          f[j][i] = uxx + uyy - sc*eu;
479:        }
480:      }
481:    }
482:    PetscLogFlops (info->xm*info->ym*(72.0));
483:    return (0);
484:  }
488:  /* 
489:      This is the opposite sign of the part of FormFunctionLocal that excludes the A(x) x part of the operation, 
490:      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) 
492:  */ 
493:  PetscErrorCode  FormFunctionPicardLocal(DMDALocalInfo  *info,PetscScalar  **x,PetscScalar  **f,AppCtx *user)494:  {
495:    PetscReal  hx,hy,sc;
496:    PetscInt   i,j;
500:    hx     = 1.0/(PetscReal )(info->mx-1);
501:    hy     = 1.0/(PetscReal )(info->my-1);
502:    sc     = hx*hy*user->lambda;
503:    /* 
504:       Compute function over the locally owned part of the grid 
505:    */ 
506:    for  (j=info->ys; j<info->ys+info->ym; j++) {
507:      for  (i=info->xs; i<info->xs+info->xm; i++) {
508:        if  (!(i == 0 || j == 0 || i == info->mx-1 || j == info->my-1)) {
509:          const PetscScalar  u = x[j][i];
510:          f[j][i] = sc*PetscExpScalar(u);
511:        }
512:      }
513:    }
514:    PetscLogFlops (info->xm*info->ym*2.0);
515:    return (0);
516:  }
520:  /* 
521:     FormJacobianLocal - Evaluates Jacobian matrix. 
522:  */ 
523:  PetscErrorCode  FormJacobianLocal(DMDALocalInfo  *info,PetscScalar  **x,Mat  J,Mat  B,AppCtx *user)524:  {
526:    PetscInt        i,j;
527:    MatStencil      col[9],row;
528:    PetscScalar     v[9];
529:    PetscReal       hx,hy,hxdhy,hydhx,dhx,dhy,sc;
532:    hx    = 1.0/(PetscReal )(info->mx-1);
533:    hy    = 1.0/(PetscReal )(info->my-1);
534:    sc    = hx*hy*user->lambda;
535:    dhx   = 1/hx;
536:    dhy   = 1/hy;
537:    hxdhy = hx/hy;
538:    hydhx = hy/hx;
540:    /* 
541:       Compute entries for the locally owned part of the Jacobian. 
542:        - PETSc parallel matrix formats are partitioned by 
543:          contiguous chunks of rows across the processors. 
544:        - Each processor needs to insert only elements that it owns 
545:          locally (but any non-local elements will be sent to the 
546:          appropriate processor during matrix assembly). 
547:        - Here, we set all entries for a particular row at once. 
548:    */ 
549:    for  (j=info->ys; j<info->ys+info->ym; j++) {
550:      for  (i=info->xs; i<info->xs+info->xm; i++) {
551:        PetscReal  xx = i*hx,yy = j*hy;
552:        row.j = j; row.i = i;
553:        /* boundary points */ 
554:        if  (i == 0 || j == 0 || i == info->mx-1 || j == info->my-1) {
555:          v[0] = 1.0;
556:          MatSetValuesStencil (B,1,&row,1,&row,v,INSERT_VALUES );
557:        } else  {
558:          /* interior grid points */ 
559:          const PetscScalar 
560:            ux_E     = dhx*(x[j][i+1]-x[j][i]),
561:            uy_E     = 0.25*dhy*(x[j+1][i]+x[j+1][i+1]-x[j-1][i]-x[j-1][i+1]),
562:            ux_W     = dhx*(x[j][i]-x[j][i-1]),
563:            uy_W     = 0.25*dhy*(x[j+1][i-1]+x[j+1][i]-x[j-1][i-1]-x[j-1][i]),
564:            ux_N     = 0.25*dhx*(x[j][i+1]+x[j+1][i+1]-x[j][i-1]-x[j+1][i-1]),
565:            uy_N     = dhy*(x[j+1][i]-x[j][i]),
566:            ux_S     = 0.25*dhx*(x[j-1][i+1]+x[j][i+1]-x[j-1][i-1]-x[j][i-1]),
567:            uy_S     = dhy*(x[j][i]-x[j-1][i]),
568:            u        = x[j][i],
569:            e_E      = eta(user,xx,yy,ux_E,uy_E),
570:            e_W      = eta(user,xx,yy,ux_W,uy_W),
571:            e_N      = eta(user,xx,yy,ux_N,uy_N),
572:            e_S      = eta(user,xx,yy,ux_S,uy_S),
573:            de_E     = deta(user,xx,yy,ux_E,uy_E),
574:            de_W     = deta(user,xx,yy,ux_W,uy_W),
575:            de_N     = deta(user,xx,yy,ux_N,uy_N),
576:            de_S     = deta(user,xx,yy,ux_S,uy_S),
577:            skew_E   = de_E*ux_E*uy_E,
578:            skew_W   = de_W*ux_W*uy_W,
579:            skew_N   = de_N*ux_N*uy_N,
580:            skew_S   = de_S*ux_S*uy_S,
581:            cross_EW = 0.25*(skew_E - skew_W),
582:            cross_NS = 0.25*(skew_N - skew_S),
583:            newt_E   = e_E+de_E*PetscSqr (ux_E),
584:            newt_W   = e_W+de_W*PetscSqr (ux_W),
585:            newt_N   = e_N+de_N*PetscSqr (uy_N),
586:            newt_S   = e_S+de_S*PetscSqr (uy_S);
587:          /* interior grid points */ 
588:          switch  (user->jtype) {
589:          case  JAC_BRATU:
590:            /* Jacobian from p=2 */ 
591:            v[0] = -hxdhy;                                           col[0].j = j-1;   col[0].i = i;
592:            v[1] = -hydhx;                                           col[1].j = j;     col[1].i = i-1;
593:            v[2] = 2.0*(hydhx + hxdhy) - sc*PetscExpScalar(u);       col[2].j = row.j; col[2].i = row.i;
594:            v[3] = -hydhx;                                           col[3].j = j;     col[3].i = i+1;
595:            v[4] = -hxdhy;                                           col[4].j = j+1;   col[4].i = i;
596:            MatSetValuesStencil (B,1,&row,5,col,v,INSERT_VALUES );
597:            break ;
598:          case  JAC_PICARD:
599:            /* Jacobian arising from Picard linearization */ 
600:            v[0] = -hxdhy*e_S;                                           col[0].j = j-1;   col[0].i = i;
601:            v[1] = -hydhx*e_W;                                           col[1].j = j;     col[1].i = i-1;
602:            v[2] = (e_W+e_E)*hydhx + (e_S+e_N)*hxdhy;                    col[2].j = row.j; col[2].i = row.i;
603:            v[3] = -hydhx*e_E;                                           col[3].j = j;     col[3].i = i+1;
604:            v[4] = -hxdhy*e_N;                                           col[4].j = j+1;   col[4].i = i;
605:            MatSetValuesStencil (B,1,&row,5,col,v,INSERT_VALUES );
606:            break ;
607:          case  JAC_STAR:
608:            /* Full Jacobian, but only a star stencil */ 
609:            col[0].j = j-1; col[0].i = i;
610:            col[1].j = j;   col[1].i = i-1;
611:            col[2].j = j;   col[2].i = i;
612:            col[3].j = j;   col[3].i = i+1;
613:            col[4].j = j+1; col[4].i = i;
614:            v[0]     = -hxdhy*newt_S + cross_EW;
615:            v[1]     = -hydhx*newt_W + cross_NS;
616:            v[2]     = hxdhy*(newt_N + newt_S) + hydhx*(newt_E + newt_W) - sc*PetscExpScalar(u);
617:            v[3]     = -hydhx*newt_E - cross_NS;
618:            v[4]     = -hxdhy*newt_N - cross_EW;
619:            MatSetValuesStencil (B,1,&row,5,col,v,INSERT_VALUES );
620:            break ;
621:          case  JAC_NEWTON:
622:            /** The Jacobian is 
623:            * 
624:            * -div [ eta (grad u) + deta (grad u0 . grad u) grad u0 ] - (eE u0) u 
625:            * 
626:            **/ 
627:            col[0].j = j-1; col[0].i = i-1;
628:            col[1].j = j-1; col[1].i = i;
629:            col[2].j = j-1; col[2].i = i+1;
630:            col[3].j = j;   col[3].i = i-1;
631:            col[4].j = j;   col[4].i = i;
632:            col[5].j = j;   col[5].i = i+1;
633:            col[6].j = j+1; col[6].i = i-1;
634:            col[7].j = j+1; col[7].i = i;
635:            col[8].j = j+1; col[8].i = i+1;
636:            v[0]     = -0.25*(skew_S + skew_W);
637:            v[1]     = -hxdhy*newt_S + cross_EW;
638:            v[2]     =  0.25*(skew_S + skew_E);
639:            v[3]     = -hydhx*newt_W + cross_NS;
640:            v[4]     = hxdhy*(newt_N + newt_S) + hydhx*(newt_E + newt_W) - sc*PetscExpScalar(u);
641:            v[5]     = -hydhx*newt_E - cross_NS;
642:            v[6]     =  0.25*(skew_N + skew_W);
643:            v[7]     = -hxdhy*newt_N - cross_EW;
644:            v[8]     = -0.25*(skew_N + skew_E);
645:            MatSetValuesStencil (B,1,&row,9,col,v,INSERT_VALUES );
646:            break ;
647:          default: 648:            SETERRQ1 (PetscObjectComm ((PetscObject )info->da),PETSC_ERR_SUP,"Jacobian type %d not implemented" ,user->jtype);
649:          }
650:        }
651:      }
652:    }
654:    /* 
655:       Assemble matrix, using the 2-step process: 
656:         MatAssemblyBegin (), MatAssemblyEnd (). 
657:    */ 
658:    MatAssemblyBegin (B,MAT_FINAL_ASSEMBLY);
659:    MatAssemblyEnd (B,MAT_FINAL_ASSEMBLY);
661:    if  (J != B) {
662:      MatAssemblyBegin (J,MAT_FINAL_ASSEMBLY);
663:      MatAssemblyEnd (J,MAT_FINAL_ASSEMBLY);
664:    }
666:    /* 
667:       Tell the matrix we will never add a new nonzero location to the 
668:       matrix. If we do, it will generate an error. 
669:    */ 
670:    if  (user->jtype == JAC_NEWTON) {
671:      PetscLogFlops (info->xm*info->ym*(131.0));
672:    }
673:    MatSetOption (B,MAT_NEW_NONZERO_LOCATION_ERR,PETSC_TRUE );
674:    return (0);
675:  }
677:  /*********************************************************** 
678:   * PreCheck implementation 
679:   ***********************************************************/ 
682:  PetscErrorCode  PreCheckSetFromOptions(PreCheck precheck)683:  {
685:    PetscBool       flg;
688:    PetscOptionsBegin (precheck->comm,NULL,"PreCheck Options" ,"none" );
689:    PetscOptionsReal ("-precheck_angle" ,"Angle in degrees between successive search directions necessary to activate step correction" ,"" ,precheck->angle,&precheck->angle,NULL);
690:    flg  = PETSC_FALSE ;
691:    PetscOptionsBool ("-precheck_monitor" ,"Monitor choices made by precheck routine" ,"" ,flg,&flg,NULL);
692:    if  (flg) {
693:      PetscViewerASCIIOpen (precheck->comm,"stdout" ,&precheck->monitor);
694:    }
695:    PetscOptionsEnd ();
696:    return (0);
697:  }
701:  /* 
702:    Compare the direction of the current and previous step, modify the current step accordingly 
703:  */ 
704:  PetscErrorCode  PreCheckFunction(SNESLineSearch  linesearch,Vec  X,Vec  Y,PetscBool  *changed, void *ctx)705:  {
707:    PreCheck       precheck;
708:    Vec             Ylast;
709:    PetscScalar     dot;
710:    PetscInt        iter;
711:    PetscReal       ynorm,ylastnorm,theta,angle_radians;
712:    SNES            snes;
715:    SNESLineSearchGetSNES (linesearch, &snes);
716:    precheck = (PreCheck)ctx;
717:    if  (!precheck->Ylast) {VecDuplicate (Y,&precheck->Ylast);}
718:    Ylast = precheck->Ylast;
719:    SNESGetIterationNumber (snes,&iter);
720:    if  (iter < 1) {
721:      VecCopy (Y,Ylast);
722:      *changed = PETSC_FALSE ;
723:      return (0);
724:    }
726:    VecDot (Y,Ylast,&dot);
727:    VecNorm (Y,NORM_2 ,&ynorm);
728:    VecNorm (Ylast,NORM_2 ,&ylastnorm);
729:    /* Compute the angle between the vectors Y and Ylast, clip to keep inside the domain of acos() */ 
730:    theta         = PetscAcosReal((PetscReal )PetscClipInterval (PetscAbsScalar(dot) / (ynorm * ylastnorm),-1.0,1.0));
731:    angle_radians = precheck->angle * PETSC_PI / 180.;
732:    if  (PetscAbsReal (theta) < angle_radians || PetscAbsReal (theta - PETSC_PI) < angle_radians) {
733:      /* Modify the step Y */ 
734:      PetscReal  alpha,ydiffnorm;
735:      VecAXPY (Ylast,-1.0,Y);
736:      VecNorm (Ylast,NORM_2 ,&ydiffnorm);
737:      alpha = ylastnorm / ydiffnorm;
738:      VecCopy (Y,Ylast);
739:      VecScale (Y,alpha);
740:      if  (precheck->monitor) {
741:        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);
742:      }
743:    } else  {
744:      VecCopy (Y,Ylast);
745:      *changed = PETSC_FALSE ;
746:      if  (precheck->monitor) {
747:        PetscViewerASCIIPrintf (precheck->monitor,"Angle %E degrees exceeds threshold %g, no correction applied\n" ,(double)(theta*180./PETSC_PI),(double)precheck->angle);
748:      }
749:    }
750:    return (0);
751:  }
755:  PetscErrorCode  PreCheckDestroy(PreCheck *precheck)756:  {
760:    if  (!*precheck) return (0);
761:    VecDestroy (&(*precheck)->Ylast);
762:    PetscViewerDestroy (&(*precheck)->monitor);
763:    PetscFree (*precheck);
764:    return (0);
765:  }
769:  PetscErrorCode  PreCheckCreate(MPI_Comm  comm,PreCheck *precheck)770:  {
774:    PetscMalloc (sizeof (struct _n_PreCheck ),precheck);
775:    PetscMemzero (*precheck,sizeof (struct _n_PreCheck ));
777:    (*precheck)->comm  = comm;
778:    (*precheck)->angle = 10.;     /* only active if angle is less than 10 degrees */ 
779:    return (0);
780:  }
784:  /* 
785:        Applies some sweeps on nonlinear Gauss-Seidel on each process 
787:   */ 
788:  PetscErrorCode  NonlinearGS(SNES  snes,Vec  X, Vec  B, void *ctx)789:  {
790:    PetscInt        i,j,k,xs,ys,xm,ym,its,tot_its,sweeps,l,m;
792:    PetscReal       hx,hy,hxdhy,hydhx,dhx,dhy,sc;
793:    PetscScalar     **x,**b,bij,F,F0=0,J,y,u,eu;
794:    PetscReal       atol,rtol,stol;
795:    DM              da;
796:    AppCtx         *user = (AppCtx*)ctx;
797:    Vec             localX,localB;
798:    DMDALocalInfo   info;
801:    SNESGetDM (snes,&da);
802:    DMDAGetLocalInfo (da,&info);
804:    hx     = 1.0/(PetscReal )(info.mx-1);
805:    hy     = 1.0/(PetscReal )(info.my-1);
806:    sc     = hx*hy*user->lambda;
807:    dhx    = 1/hx;
808:    dhy    = 1/hy;
809:    hxdhy  = hx/hy;
810:    hydhx  = hy/hx;
812:    tot_its = 0;
813:    SNESNGSGetSweeps (snes,&sweeps);
814:    SNESNGSGetTolerances (snes,&atol,&rtol,&stol,&its);
815:    DMGetLocalVector (da,&localX);
816:    if  (B) {
817:      DMGetLocalVector (da,&localB);
818:    }
819:    if  (B) {
820:      DMGlobalToLocalBegin (da,B,INSERT_VALUES ,localB);
821:      DMGlobalToLocalEnd (da,B,INSERT_VALUES ,localB);
822:    }
823:    if  (B) DMDAVecGetArrayRead (da,localB,&b);
824:    DMGlobalToLocalBegin (da,X,INSERT_VALUES ,localX);
825:    DMGlobalToLocalEnd (da,X,INSERT_VALUES ,localX);
826:    DMDAVecGetArray (da,localX,&x);
827:    for  (l=0; l<sweeps; l++) {
828:      /* 
829:       Get local grid boundaries (for 2-dimensional DMDA ): 
830:       xs, ys   - starting grid indices (no ghost points) 
831:       xm, ym   - widths of local grid (no ghost points) 
832:       */ 
833:      DMDAGetCorners (da,&xs,&ys,NULL,&xm,&ym,NULL);
834:      for  (m=0; m<2; m++) {
835:        for  (j=ys; j<ys+ym; j++) {
836:          for  (i=xs+(m+j)%2; i<xs+xm; i+=2) {
837:            PetscReal  xx = i*hx,yy = j*hy;
838:            if  (B) bij = b[j][i];
839:            else    bij = 0.;
841:            if  (i == 0 || j == 0 || i == info.mx-1 || j == info.my-1) {
842:              /* boundary conditions are all zero Dirichlet */ 
843:              x[j][i] = 0.0 + bij;
844:            } else  {
845:              const PetscScalar 
846:                u_E = x[j][i+1],
847:                u_W = x[j][i-1],
848:                u_N = x[j+1][i],
849:                u_S = x[j-1][i];
850:              const PetscScalar 
851:                uy_E   = 0.25*dhy*(x[j+1][i]+x[j+1][i+1]-x[j-1][i]-x[j-1][i+1]),
852:                uy_W   = 0.25*dhy*(x[j+1][i-1]+x[j+1][i]-x[j-1][i-1]-x[j-1][i]),
853:                ux_N   = 0.25*dhx*(x[j][i+1]+x[j+1][i+1]-x[j][i-1]-x[j+1][i-1]),
854:                ux_S   = 0.25*dhx*(x[j-1][i+1]+x[j][i+1]-x[j-1][i-1]-x[j][i-1]);
855:              u = x[j][i];
856:              for  (k=0; k<its; k++) {
857:                const PetscScalar 
858:                  ux_E   = dhx*(u_E-u),
859:                  ux_W   = dhx*(u-u_W),
860:                  uy_N   = dhy*(u_N-u),
861:                  uy_S   = dhy*(u-u_S),
862:                  e_E    = eta(user,xx,yy,ux_E,uy_E),
863:                  e_W    = eta(user,xx,yy,ux_W,uy_W),
864:                  e_N    = eta(user,xx,yy,ux_N,uy_N),
865:                  e_S    = eta(user,xx,yy,ux_S,uy_S),
866:                  de_E   = deta(user,xx,yy,ux_E,uy_E),
867:                  de_W   = deta(user,xx,yy,ux_W,uy_W),
868:                  de_N   = deta(user,xx,yy,ux_N,uy_N),
869:                  de_S   = deta(user,xx,yy,ux_S,uy_S),
870:                  newt_E = e_E+de_E*PetscSqr (ux_E),
871:                  newt_W = e_W+de_W*PetscSqr (ux_W),
872:                  newt_N = e_N+de_N*PetscSqr (uy_N),
873:                  newt_S = e_S+de_S*PetscSqr (uy_S),
874:                  uxx    = -hy * (e_E*ux_E - e_W*ux_W),
875:                  uyy    = -hx * (e_N*uy_N - e_S*uy_S);
877:                if  (sc) eu = PetscExpScalar(u);
878:                else     eu = 0;
880:                F = uxx + uyy - sc*eu - bij;
881:                if  (k == 0) F0 = F;
882:                J  = hxdhy*(newt_N + newt_S) + hydhx*(newt_E + newt_W) - sc*eu;
883:                y  = F/J;
884:                u -= y;
885:                tot_its++;
886:                if  (atol > PetscAbsReal (PetscRealPart(F)) ||
887:                    rtol*PetscAbsReal (PetscRealPart(F0)) > PetscAbsReal (PetscRealPart(F)) ||
888:                    stol*PetscAbsReal (PetscRealPart(u)) > PetscAbsReal (PetscRealPart(y))) {
889:                  break ;
890:                }
891:              }
892:              x[j][i] = u;
893:            }
894:          }
895:        }
896:      }
897:      /* 
898:  x     Restore vector 
899:       */ 
900:    }
901:    DMDAVecRestoreArray (da,localX,&x);
902:    DMLocalToGlobalBegin (da,localX,INSERT_VALUES ,X);
903:    DMLocalToGlobalEnd (da,localX,INSERT_VALUES ,X);
904:    PetscLogFlops (tot_its*(118.0));
905:    DMRestoreLocalVector (da,&localX);
906:    if  (B) {
907:      DMDAVecRestoreArrayRead (da,localB,&b);
908:      DMRestoreLocalVector (da,&localB);
909:    }
910:    return (0);
911:  }