Actual source code: ex5.c

  1: /*$Id: ex5.c,v 1.132 2001/04/10 19:37:05 bsmith Exp $*/

  3: /* Program usage:  mpirun -np <procs> ex5 [-help] [all PETSc options] */

  5: static char help[] = "Bratu nonlinear PDE in 2d.n
  6: We solve the  Bratu (SFI - solid fuel ignition) problem in a 2D rectangularn
  7: domain, using distributed arrays (DAs) to partition the parallel grid.n
  8: The command line options include:n
  9:   -par <parameter>, where <parameter> indicates the problem's nonlinearityn
 10:      problem SFI:  <parameter> = Bratu parameter (0 <= par <= 6.81)nn";

 12: /*T
 13:    Concepts: SNES^parallel Bratu example
 14:    Concepts: DA^using distributed arrays;
 15:    Processors: n
 16: T*/

 18: /* ------------------------------------------------------------------------

 20:     Solid Fuel Ignition (SFI) problem.  This problem is modeled by
 21:     the partial differential equation
 22:   
 23:             -Laplacian u - lambda*exp(u) = 0,  0 < x,y < 1,
 24:   
 25:     with boundary conditions
 26:    
 27:              u = 0  for  x = 0, x = 1, y = 0, y = 1.
 28:   
 29:     A finite difference approximation with the usual 5-point stencil
 30:     is used to discretize the boundary value problem to obtain a nonlinear 
 31:     system of equations.


 34:   ------------------------------------------------------------------------- */

 36: /* 
 37:    Include "petscda.h" so that we can use distributed arrays (DAs).
 38:    Include "petscsnes.h" so that we can use SNES solvers.  Note that this
 39:    file automatically includes:
 40:      petsc.h       - base PETSc routines   petscvec.h - vectors
 41:      petscsys.h    - system routines       petscmat.h - matrices
 42:      petscis.h     - index sets            petscksp.h - Krylov subspace methods
 43:      petscviewer.h - viewers               petscpc.h  - preconditioners
 44:      petscsles.h   - linear solvers
 45: */
 46: #include "petscda.h"
 47: #include "petscsnes.h"

 49: /* 
 50:    User-defined application context - contains data needed by the 
 51:    application-provided call-back routines, FormJacobian() and
 52:    FormFunction().
 53: */
 54: typedef struct {
 55:    double      param;          /* test problem parameter */
 56:    DA          da;             /* distributed array data structure */
 57: } AppCtx;

 59: /* 
 60:    User-defined routines
 61: */
 62: extern int FormFunction(SNES,Vec,Vec,void*),FormInitialGuess(AppCtx*,Vec),FormFunctionMatlab(SNES,Vec,Vec,void*);
 63: extern int FormJacobian(SNES,Vec,Mat*,Mat*,MatStructure*,void*);

 65: int main(int argc,char **argv)
 66: {
 67:   SNES                   snes;                 /* nonlinear solver */
 68:   Vec                    x,r;                  /* solution, residual vectors */
 69:   Mat                    J;                    /* Jacobian matrix */
 70:   AppCtx                 user;                 /* user-defined work context */
 71:   int                    its;                  /* iterations for convergence */
 72:   PetscTruth             matrix_free,coloring;
 73: #if defined(PETSC_HAVE_MATLAB_ENGINE) && !defined(PETSC_USE_COMPLEX)
 74:   PetscTruth             matlab;
 75: #endif
 76:   int                    ierr;
 77:   double                 bratu_lambda_max = 6.81,bratu_lambda_min = 0.,fnorm;
 78:   MatFDColoring          matfdcoloring;

 80:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 81:      Initialize program
 82:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

 84:   PetscInitialize(&argc,&argv,(char *)0,help);

 86:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 87:      Initialize problem parameters
 88:   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 89:   user.param = 6.0;
 90:   PetscOptionsGetDouble(PETSC_NULL,"-par",&user.param,PETSC_NULL);
 91:   if (user.param >= bratu_lambda_max || user.param <= bratu_lambda_min) {
 92:     SETERRQ(1,"Lambda is out of range");
 93:   }

 95:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 96:      Create nonlinear solver context
 97:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 98:   SNESCreate(PETSC_COMM_WORLD,SNES_NONLINEAR_EQUATIONS,&snes);

100:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
101:      Create distributed array (DA) to manage parallel grid and vectors
102:   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
103:   DACreate2d(PETSC_COMM_WORLD,DA_NONPERIODIC,DA_STENCIL_STAR,4,4,PETSC_DECIDE,PETSC_DECIDE,
104:                     1,1,PETSC_NULL,PETSC_NULL,&user.da);

106:   /*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
107:      Extract global vectors from DA; then duplicate for remaining
108:      vectors that are the same types
109:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
110:   DACreateGlobalVector(user.da,&x);
111:   VecDuplicate(x,&r);

113:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
114:      Set function evaluation routine and vector
115:   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
116: #if defined(PETSC_HAVE_MATLAB_ENGINE) && !defined(PETSC_USE_COMPLEX)
117:   PetscOptionsHasName(PETSC_NULL,"-matlab",&matlab);
118:   if (matlab) {
119:     SNESSetFunction(snes,r,FormFunctionMatlab,(void*)&user);
120:   } else {
121:     SNESSetFunction(snes,r,FormFunction,(void*)&user);
122:   }
123: #else
124:   SNESSetFunction(snes,r,FormFunction,(void*)&user);
125: #endif

127:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
128:      Create matrix data structure; set Jacobian evaluation routine

130:      Set Jacobian matrix data structure and default Jacobian evaluation
131:      routine. User can override with:
132:      -snes_mf : matrix-free Newton-Krylov method with no preconditioning
133:                 (unless user explicitly sets preconditioner) 
134:      -snes_mf_operator : form preconditioning matrix as set by the user,
135:                          but use matrix-free approx for Jacobian-vector
136:                          products within Newton-Krylov method
137:      -fdcoloring : using finite differences with coloring to compute the Jacobian

139:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
140:   PetscOptionsHasName(PETSC_NULL,"-snes_mf",&matrix_free);
141:   PetscOptionsHasName(PETSC_NULL,"-fdcoloring",&coloring);
142:   if (!matrix_free) {
143:     if (coloring) {
144:       ISColoring    iscoloring;

146:       DAGetColoring(user.da,IS_COLORING_GLOBAL,MATMPIAIJ,&iscoloring,&J);
147:       MatFDColoringCreate(J,iscoloring,&matfdcoloring);
148:       ISColoringDestroy(iscoloring);
149:       MatFDColoringSetFunction(matfdcoloring,(int (*)(void))FormFunction,&user);
150:       MatFDColoringSetFromOptions(matfdcoloring);
151:       SNESSetJacobian(snes,J,J,SNESDefaultComputeJacobianColor,matfdcoloring);
152:     } else {
153:       DAGetColoring(user.da,IS_COLORING_GLOBAL,MATMPIAIJ,PETSC_IGNORE,&J);
154:       SNESSetJacobian(snes,J,J,FormJacobian,&user);
155:     }
156:   }

158:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
159:      Customize nonlinear solver; set runtime options
160:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
161:   SNESSetFromOptions(snes);

163:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
164:      Evaluate initial guess
165:      Note: The user should initialize the vector, x, with the initial guess
166:      for the nonlinear solver prior to calling SNESSolve().  In particular,
167:      to employ an initial guess of zero, the user should explicitly set
168:      this vector to zero by calling VecSet().
169:   */
170:   FormInitialGuess(&user,x);

172:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
173:      Solve nonlinear system
174:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
175:   SNESSolve(snes,x,&its);

177:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
178:      Explicitly check norm of the residual of the solution
179:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
180:   FormFunction(snes,x,r,(void *)&user);
181:   VecNorm(r,NORM_2,&fnorm);
182:   PetscPrintf(PETSC_COMM_WORLD,"Number of Newton iterations = %d fnorm %gn",its,fnorm);

184:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
185:      Free work space.  All PETSc objects should be destroyed when they
186:      are no longer needed.
187:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

189:   if (!matrix_free) {
190:     MatDestroy(J);
191:   }
192:   if (coloring) {
193:     MatFDColoringDestroy(matfdcoloring);
194:   }
195:   VecDestroy(x);
196:   VecDestroy(r);
197:   SNESDestroy(snes);
198:   DADestroy(user.da);
199:   PetscFinalize();

201:   return(0);
202: }
203: /* ------------------------------------------------------------------- */
204: /* 
205:    FormInitialGuess - Forms initial approximation.

207:    Input Parameters:
208:    user - user-defined application context
209:    X - vector

211:    Output Parameter:
212:    X - vector
213:  */
214: int FormInitialGuess(AppCtx *user,Vec X)
215: {
216:   int     i,j,Mx,My,ierr,xs,ys,xm,ym;
217:   double  lambda,temp1,temp,hx,hy;
218:   Scalar  **x;

221:   DAGetInfo(user->da,PETSC_IGNORE,&Mx,&My,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,
222:                    PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE);

224:   lambda = user->param;
225:   hx     = 1.0/(double)(Mx-1);
226:   hy     = 1.0/(double)(My-1);
227:   temp1  = lambda/(lambda + 1.0);

229:   /*
230:      Get a pointer to vector data.
231:        - For default PETSc vectors, VecGetArray() returns a pointer to
232:          the data array.  Otherwise, the routine is implementation dependent.
233:        - You MUST call VecRestoreArray() when you no longer need access to
234:          the array.
235:   */
236:   DAVecGetArray(user->da,X,(void**)&x);

238:   /*
239:      Get local grid boundaries (for 2-dimensional DA):
240:        xs, ys   - starting grid indices (no ghost points)
241:        xm, ym   - widths of local grid (no ghost points)

243:   */
244:   DAGetCorners(user->da,&xs,&ys,PETSC_NULL,&xm,&ym,PETSC_NULL);

246:   /*
247:      Compute initial guess over the locally owned part of the grid
248:   */
249:   for (j=ys; j<ys+ym; j++) {
250:     temp = (double)(PetscMin(j,My-j-1))*hy;
251:     for (i=xs; i<xs+xm; i++) {

253:       if (i == 0 || j == 0 || i == Mx-1 || j == My-1) {
254:         /* boundary conditions are all zero Dirichlet */
255:         x[j][i] = 0.0;
256:       } else {
257:         x[j][i] = temp1*sqrt(PetscMin((double)(PetscMin(i,Mx-i-1))*hx,temp));
258:       }
259:     }
260:   }

262:   /*
263:      Restore vector
264:   */
265:   DAVecRestoreArray(user->da,X,(void**)&x);

267:   return(0);
268: }
269: /* ------------------------------------------------------------------- */
270: /* 
271:    FormFunction - Evaluates nonlinear function, F(x).

273:    Input Parameters:
274: .  snes - the SNES context
275: .  X - input vector
276: .  ptr - optional user-defined context, as set by SNESSetFunction()

278:    Output Parameter:
279: .  F - function vector
280:  */
281: int FormFunction(SNES snes,Vec X,Vec F,void *ptr)
282: {
283:   AppCtx  *user = (AppCtx*)ptr;
284:   int     ierr,i,j,Mx,My,xs,ys,xm,ym;
285:   double  two = 2.0,lambda,hx,hy,hxdhy,hydhx,sc;
286:   Scalar  u,uxx,uyy,**x,**f;
287:   Vec     localX;

290:   DAGetLocalVector(user->da,&localX);
291:   DAGetInfo(user->da,PETSC_IGNORE,&Mx,&My,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,
292:                    PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE);

294:   lambda = user->param;
295:   hx     = 1.0/(double)(Mx-1);
296:   hy     = 1.0/(double)(My-1);
297:   sc     = hx*hy*lambda;
298:   hxdhy  = hx/hy;
299:   hydhx  = hy/hx;

301:   /*
302:      Scatter ghost points to local vector,using the 2-step process
303:         DAGlobalToLocalBegin(),DAGlobalToLocalEnd().
304:      By placing code between these two statements, computations can be
305:      done while messages are in transition.
306:   */
307:   DAGlobalToLocalBegin(user->da,X,INSERT_VALUES,localX);
308:   DAGlobalToLocalEnd(user->da,X,INSERT_VALUES,localX);

310:   /*
311:      Get pointers to vector data
312:   */
313:   DAVecGetArray(user->da,localX,(void**)&x);
314:   DAVecGetArray(user->da,F,(void**)&f);

316:   /*
317:      Get local grid boundaries
318:   */
319:   DAGetCorners(user->da,&xs,&ys,PETSC_NULL,&xm,&ym,PETSC_NULL);

321:   /*
322:      Compute function over the locally owned part of the grid
323:   */
324:   for (j=ys; j<ys+ym; j++) {
325:     for (i=xs; i<xs+xm; i++) {
326:       if (i == 0 || j == 0 || i == Mx-1 || j == My-1) {
327:         f[j][i] = x[j][i];
328:         continue;
329:       }
330:       u       = x[j][i];
331:       uxx     = (two*u - x[j][i-1] - x[j][i+1])*hydhx;
332:       uyy     = (two*u - x[j-1][i] - x[j+1][i])*hxdhy;
333:       f[j][i] = uxx + uyy - sc*PetscExpScalar(u);
334:     }
335:   }

337:   /*
338:      Restore vectors
339:   */
340:   DAVecRestoreArray(user->da,localX,(void**)&x);
341:   DAVecRestoreArray(user->da,F,(void**)&f);
342:   DARestoreLocalVector(user->da,&localX);
343:   PetscLogFlops(11*ym*xm);
344:   return(0);
345: }
346: /* ------------------------------------------------------------------- */
347: /*
348:    FormJacobian - Evaluates Jacobian matrix.

350:    Input Parameters:
351: .  snes - the SNES context
352: .  x - input vector
353: .  ptr - optional user-defined context, as set by SNESSetJacobian()

355:    Output Parameters:
356: .  A - Jacobian matrix
357: .  B - optionally different preconditioning matrix
358: .  flag - flag indicating matrix structure

360: */
361: int FormJacobian(SNES snes,Vec X,Mat *J,Mat *B,MatStructure *flag,void *ptr)
362: {
363:   AppCtx     *user = (AppCtx*)ptr;  /* user-defined application context */
364:   Mat        jac = *B;                /* Jacobian matrix */
365:   Vec        localX;
366:   int        ierr,i,j;
367:   MatStencil col[5],row;
368:   int        xs,ys,xm,ym,Mx,My;
369:   Scalar     lambda,v[5],hx,hy,hxdhy,hydhx,sc,**x;

372:   DAGetLocalVector(user->da,&localX);
373:   DAGetInfo(user->da,PETSC_IGNORE,&Mx,&My,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,
374:                    PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE);

376:   lambda = user->param;
377:   hx     = 1.0/(double)(Mx-1);
378:   hy     = 1.0/(double)(My-1);
379:   sc     = hx*hy*lambda;
380:   hxdhy  = hx/hy;
381:   hydhx  = hy/hx;


384:   /*
385:      Scatter ghost points to local vector, using the 2-step process
386:         DAGlobalToLocalBegin(), DAGlobalToLocalEnd().
387:      By placing code between these two statements, computations can be
388:      done while messages are in transition.
389:   */
390:   DAGlobalToLocalBegin(user->da,X,INSERT_VALUES,localX);
391:   DAGlobalToLocalEnd(user->da,X,INSERT_VALUES,localX);

393:   /*
394:      Get pointer to vector data
395:   */
396:   DAVecGetArray(user->da,localX,(void**)&x);

398:   /*
399:      Get local grid boundaries
400:   */
401:   DAGetCorners(user->da,&xs,&ys,PETSC_NULL,&xm,&ym,PETSC_NULL);

403:   /* 
404:      Compute entries for the locally owned part of the Jacobian.
405:       - Currently, all PETSc parallel matrix formats are partitioned by
406:         contiguous chunks of rows across the processors. 
407:       - Each processor needs to insert only elements that it owns
408:         locally (but any non-local elements will be sent to the
409:         appropriate processor during matrix assembly). 
410:       - Here, we set all entries for a particular row at once.
411:       - We can set matrix entries either using either
412:         MatSetValuesLocal() or MatSetValues(), as discussed above.
413:   */
414:   for (j=ys; j<ys+ym; j++) {
415:     for (i=xs; i<xs+xm; i++) {
416:       row.j = j; row.i = i;
417:       /* boundary points */
418:       if (i == 0 || j == 0 || i == Mx-1 || j == My-1) {
419:         v[0] = 1.0;
420:         MatSetValuesStencil(jac,1,&row,1,&row,v,INSERT_VALUES);
421:       } else {
422:       /* interior grid points */
423:         v[0] = -hxdhy;                                           col[0].j = j - 1; col[0].i = i;
424:         v[1] = -hydhx;                                           col[1].j = j;     col[1].i = i-1;
425:         v[2] = 2.0*(hydhx + hxdhy) - sc*PetscExpScalar(x[j][i]); col[2].j = row.j; col[2].i = row.i;
426:         v[3] = -hydhx;                                           col[3].j = j;     col[3].i = i+1;
427:         v[4] = -hxdhy;                                           col[4].j = j + 1; col[4].i = i;
428:         MatSetValuesStencil(jac,1,&row,5,col,v,INSERT_VALUES);
429:       }
430:     }
431:   }
432:   DAVecRestoreArray(user->da,localX,(void**)&x);
433:   DARestoreLocalVector(user->da,&localX);

435:   /* 
436:      Assemble matrix, using the 2-step process:
437:        MatAssemblyBegin(), MatAssemblyEnd().
438:   */
439:   MatAssemblyBegin(jac,MAT_FINAL_ASSEMBLY);
440:   MatAssemblyEnd(jac,MAT_FINAL_ASSEMBLY);

442:   /*
443:      Normally since the matrix has already been assembled above; this
444:      would do nothing. But in the matrix free mode -snes_mf_operator
445:      this tells the "matrix-free" matrix that a new linear system solve
446:      is about to be done.
447:   */

449:   MatAssemblyBegin(*J,MAT_FINAL_ASSEMBLY);
450:   MatAssemblyEnd(*J,MAT_FINAL_ASSEMBLY);

452:   /*
453:      Set flag to indicate that the Jacobian matrix retains an identical
454:      nonzero structure throughout all nonlinear iterations (although the
455:      values of the entries change). Thus, we can save some work in setting
456:      up the preconditioner (e.g., no need to redo symbolic factorization for
457:      ILU/ICC preconditioners).
458:       - If the nonzero structure of the matrix is different during
459:         successive linear solves, then the flag DIFFERENT_NONZERO_PATTERN
460:         must be used instead.  If you are unsure whether the matrix
461:         structure has changed or not, use the flag DIFFERENT_NONZERO_PATTERN.
462:       - Caution:  If you specify SAME_NONZERO_PATTERN, PETSc
463:         believes your assertion and does not check the structure
464:         of the matrix.  If you erroneously claim that the structure
465:         is the same when it actually is not, the new preconditioner
466:         will not function correctly.  Thus, use this optimization
467:         feature with caution!
468:   */
469:   *flag = SAME_NONZERO_PATTERN;


472:   /*
473:      Tell the matrix we will never add a new nonzero location to the
474:      matrix. If we do, it will generate an error.
475:   */
476:   MatSetOption(jac,MAT_NEW_NONZERO_LOCATION_ERR);
477:   return(0);
478: }

480: /*
481:       Variant of FormFunction() that computes the function in Matlab
482: */
483: #if defined(PETSC_HAVE_MATLAB_ENGINE) && !defined(PETSC_USE_COMPLEX)
484: int FormFunctionMatlab(SNES snes,Vec X,Vec F,void *ptr)
485: {
486:   AppCtx   *user = (AppCtx*)ptr;
487:   int      ierr,Mx,My;
488:   double   lambda,hx,hy;
489:   Vec      localX,localF;
490:   MPI_Comm comm;

493:   DAGetLocalVector(user->da,&localX);
494:   DAGetLocalVector(user->da,&localF);
495:   PetscObjectSetName((PetscObject)localX,"localX");
496:   PetscObjectSetName((PetscObject)localF,"localF");
497:   DAGetInfo(user->da,PETSC_IGNORE,&Mx,&My,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,
498:                    PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE);

500:   lambda = user->param;
501:   hx     = 1.0/(double)(Mx-1);
502:   hy     = 1.0/(double)(My-1);

504:   PetscObjectGetComm((PetscObject)snes,&comm);
505:   /*
506:      Scatter ghost points to local vector,using the 2-step process
507:         DAGlobalToLocalBegin(),DAGlobalToLocalEnd().
508:      By placing code between these two statements, computations can be
509:      done while messages are in transition.
510:   */
511:   DAGlobalToLocalBegin(user->da,X,INSERT_VALUES,localX);
512:   DAGlobalToLocalEnd(user->da,X,INSERT_VALUES,localX);
513:   PetscMatlabEnginePut(MATLAB_ENGINE_(comm),(PetscObject)localX);
514:   PetscMatlabEngineEvaluate(MATLAB_ENGINE_(comm),"localF=ex5m(localX,%18.16e,%18.16e,%18.16e)",hx,hy,lambda);
515:   PetscMatlabEngineGet(MATLAB_ENGINE_(comm),(PetscObject)localF);

517:   /*
518:      Insert values into global vector
519:   */
520:   DALocalToGlobal(user->da,localF,INSERT_VALUES,F);
521:   DARestoreLocalVector(user->da,&localX);
522:   DARestoreLocalVector(user->da,&localF);
523:   return(0);
524: }
525: #endif