Actual source code: ex14.c

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

  4: static char help[] = "Solves a nonlinear system in parallel with a user-defined Newton method.\n\
  5: Uses KSP to solve the linearized Newton sytems.  This solver\n\
  6: is a very simplistic inexact Newton method.  The intent of this code is to\n\
  7: demonstrate the repeated solution of linear sytems with the same nonzero pattern.\n\
  8: \n\
  9: This is NOT the recommended approach for solving nonlinear problems with PETSc!\n\
 10: We urge users to employ the SNES component for solving nonlinear problems whenever\n\
 11: possible, as it offers many advantages over coding nonlinear solvers independently.\n\
 12: \n\
 13: We solve the  Bratu (SFI - solid fuel ignition) problem in a 2D rectangular\n\
 14: domain, using distributed arrays (DAs) to partition the parallel grid.\n\
 15: The command line options include:\n\
 16:   -par <parameter>, where <parameter> indicates the problem's nonlinearity\n\
 17:      problem SFI:  <parameter> = Bratu parameter (0 <= par <= 6.81)\n\
 18:   -mx <xg>, where <xg> = number of grid points in the x-direction\n\
 19:   -my <yg>, where <yg> = number of grid points in the y-direction\n\
 20:   -Nx <npx>, where <npx> = number of processors in the x-direction\n\
 21:   -Ny <npy>, where <npy> = number of processors in the y-direction\n\n";

 23: /*T
 24:    Concepts: KSP^writing a user-defined nonlinear solver (parallel Bratu example);
 25:    Concepts: DA^using distributed arrays;
 26:    Processors: n
 27: T*/

 29: /* ------------------------------------------------------------------------

 31:     Solid Fuel Ignition (SFI) problem.  This problem is modeled by
 32:     the partial differential equation
 33:   
 34:             -Laplacian u - lambda*exp(u) = 0,  0 < x,y < 1,
 35:   
 36:     with boundary conditions
 37:    
 38:              u = 0  for  x = 0, x = 1, y = 0, y = 1.
 39:   
 40:     A finite difference approximation with the usual 5-point stencil
 41:     is used to discretize the boundary value problem to obtain a nonlinear 
 42:     system of equations.

 44:     The SNES version of this problem is:  snes/examples/tutorials/ex5.c
 45:     We urge users to employ the SNES component for solving nonlinear
 46:     problems whenever possible, as it offers many advantages over coding 
 47:     nonlinear solvers independently.

 49:   ------------------------------------------------------------------------- */

 51: /* 
 52:    Include "petscda.h" so that we can use distributed arrays (DAs).
 53:    Include "petscksp.h" so that we can use KSP solvers.  Note that this
 54:    file automatically includes:
 55:      petsc.h       - base PETSc routines   petscvec.h - vectors
 56:      petscsys.h    - system routines       petscmat.h - matrices
 57:      petscis.h     - index sets            petscksp.h - Krylov subspace methods
 58:      petscviewer.h - viewers               petscpc.h  - preconditioners
 59: */
 60:  #include petscda.h
 61:  #include petscksp.h

 63: /* 
 64:    User-defined application context - contains data needed by the 
 65:    application-provided call-back routines, ComputeJacobian() and
 66:    ComputeFunction().
 67: */
 68: typedef struct {
 69:    PetscReal   param;          /* test problem parameter */
 70:    PetscInt    mx,my;          /* discretization in x,y directions */
 71:    Vec         localX,localF; /* ghosted local vector */
 72:    DA          da;             /* distributed array data structure */
 73:    PetscInt    rank;           /* processor rank */
 74: } AppCtx;

 76: /* 
 77:    User-defined routines
 78: */

 84: int main(int argc,char **argv)
 85: {
 86:   /* -------------- Data to define application problem ---------------- */
 87:   MPI_Comm       comm;                /* communicator */
 88:   KSP            ksp;                /* linear solver */
 89:   Vec            X,Y,F;             /* solution, update, residual vectors */
 90:   Mat            J;                   /* Jacobian matrix */
 91:   AppCtx         user;                /* user-defined work context */
 92:   PetscInt       Nx,Ny;              /* number of preocessors in x- and y- directions */
 93:   PetscMPIInt    size;                /* number of processors */
 94:   PetscReal      bratu_lambda_max = 6.81,bratu_lambda_min = 0.;
 95:   PetscInt       m,N;

 98:   /* --------------- Data to define nonlinear solver -------------- */
 99:   PetscReal      rtol = 1.e-8;        /* relative convergence tolerance */
100:   PetscReal      xtol = 1.e-8;        /* step convergence tolerance */
101:   PetscReal      ttol;                /* convergence tolerance */
102:   PetscReal      fnorm,ynorm,xnorm; /* various vector norms */
103:   PetscInt       max_nonlin_its = 10; /* maximum number of iterations for nonlinear solver */
104:   PetscInt       max_functions = 50;  /* maximum number of function evaluations */
105:   PetscInt       lin_its;             /* number of linear solver iterations for each step */
106:   PetscInt       i;                   /* nonlinear solve iteration number */
107:   MatStructure   mat_flag;        /* flag indicating structure of preconditioner matrix */
108:   PetscTruth     no_output;           /* flag indicating whether to surpress output */
109:   PetscScalar    mone = -1.0;

111:   PetscInitialize(&argc,&argv,(char *)0,help);
112:   comm = PETSC_COMM_WORLD;
113:   MPI_Comm_rank(comm,&user.rank);
114:   PetscOptionsHasName(PETSC_NULL,"-no_output",&no_output);

116:   /*
117:      Initialize problem parameters
118:   */
119:   user.mx = 4; user.my = 4; user.param = 6.0;
120:   PetscOptionsGetInt(PETSC_NULL,"-mx",&user.mx,PETSC_NULL);
121:   PetscOptionsGetInt(PETSC_NULL,"-my",&user.my,PETSC_NULL);
122:   PetscOptionsGetReal(PETSC_NULL,"-par",&user.param,PETSC_NULL);
123:   if (user.param >= bratu_lambda_max || user.param <= bratu_lambda_min) {
124:     SETERRQ(1,"Lambda is out of range");
125:   }
126:   N = user.mx*user.my;

128:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
129:      Create linear solver context
130:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

132:   KSPCreate(comm,&ksp);

134:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
135:      Create vector data structures
136:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

138:   /*
139:      Create distributed array (DA) to manage parallel grid and vectors
140:   */
141:   MPI_Comm_size(comm,&size);
142:   Nx = PETSC_DECIDE; Ny = PETSC_DECIDE;
143:   PetscOptionsGetInt(PETSC_NULL,"-Nx",&Nx,PETSC_NULL);
144:   PetscOptionsGetInt(PETSC_NULL,"-Ny",&Ny,PETSC_NULL);
145:   if (Nx*Ny != size && (Nx != PETSC_DECIDE || Ny != PETSC_DECIDE))
146:     SETERRQ(1,"Incompatible number of processors:  Nx * Ny != size");
147:   DACreate2d(comm,DA_NONPERIODIC,DA_STENCIL_STAR,user.mx,
148:                     user.my,Nx,Ny,1,1,PETSC_NULL,PETSC_NULL,&user.da);

150:   /*
151:      Extract global and local vectors from DA; then duplicate for remaining
152:      vectors that are the same types
153:   */
154:   DACreateGlobalVector(user.da,&X);
155:   DACreateLocalVector(user.da,&user.localX);
156:   VecDuplicate(X,&F);
157:   VecDuplicate(X,&Y);
158:   VecDuplicate(user.localX,&user.localF);


161:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
162:      Create matrix data structure for Jacobian
163:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
164:   /*
165:      Note:  For the parallel case, vectors and matrices MUST be partitioned
166:      accordingly.  When using distributed arrays (DAs) to create vectors,
167:      the DAs determine the problem partitioning.  We must explicitly
168:      specify the local matrix dimensions upon its creation for compatibility
169:      with the vector distribution.  Thus, the generic MatCreate() routine
170:      is NOT sufficient when working with distributed arrays.

172:      Note: Here we only approximately preallocate storage space for the
173:      Jacobian.  See the users manual for a discussion of better techniques
174:      for preallocating matrix memory.
175:   */
176:   if (size == 1) {
177:     MatCreateSeqAIJ(comm,N,N,5,PETSC_NULL,&J);
178:   } else {
179:     VecGetLocalSize(X,&m);
180:     MatCreateMPIAIJ(comm,m,m,N,N,5,PETSC_NULL,3,PETSC_NULL,&J);
181:   }

183:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
184:      Customize linear solver; set runtime options
185:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

187:   /*
188:      Set runtime options (e.g.,-ksp_monitor -ksp_rtol <rtol> -ksp_type <type>)
189:   */
190:   KSPSetFromOptions(ksp);

192:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
193:      Evaluate initial guess
194:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

196:   FormInitialGuess(&user,X);
197:   ComputeFunction(&user,X,F);   /* Compute F(X)    */
198:   VecNorm(F,NORM_2,&fnorm);     /* fnorm = || F || */
199:   ttol = fnorm*rtol;
200:   if (!no_output) PetscPrintf(comm,"Initial function norm = %g\n",fnorm);

202:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
203:      Solve nonlinear system with a user-defined method
204:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

206:   /* 
207:       This solver is a very simplistic inexact Newton method, with no
208:       no damping strategies or bells and whistles. The intent of this code
209:       is  merely to demonstrate the repeated solution with KSP of linear
210:       sytems with the same nonzero structure.

212:       This is NOT the recommended approach for solving nonlinear problems
213:       with PETSc!  We urge users to employ the SNES component for solving
214:       nonlinear problems whenever possible with application codes, as it
215:       offers many advantages over coding nonlinear solvers independently.
216:    */

218:   for (i=0; i<max_nonlin_its; i++) {

220:     /* 
221:         Compute the Jacobian matrix.  See the comments in this routine for
222:         important information about setting the flag mat_flag.
223:      */
224:     ComputeJacobian(&user,X,J,&mat_flag);

226:     /* 
227:         Solve J Y = F, where J is the Jacobian matrix.
228:           - First, set the KSP linear operators.  Here the matrix that
229:             defines the linear system also serves as the preconditioning
230:             matrix.
231:           - Then solve the Newton system.
232:      */
233:     KSPSetOperators(ksp,J,J,mat_flag);
234:     KSPSolve(ksp,F,Y);
235:     KSPGetIterationNumber(ksp,&lin_its);

237:     /* 
238:        Compute updated iterate
239:      */
240:     VecNorm(Y,NORM_2,&ynorm);       /* ynorm = || Y || */
241:     VecAYPX(Y,mone,X);              /* Y <- X - Y      */
242:     VecCopy(Y,X);                   /* X <- Y          */
243:     VecNorm(X,NORM_2,&xnorm);       /* xnorm = || X || */
244:     if (!no_output) {
245:       PetscPrintf(comm,"   linear solve iterations = %D, xnorm=%g, ynorm=%g\n",lin_its,xnorm,ynorm);
246:     }

248:     /* 
249:        Evaluate new nonlinear function
250:      */
251:     ComputeFunction(&user,X,F);     /* Compute F(X)    */
252:     VecNorm(F,NORM_2,&fnorm);       /* fnorm = || F || */
253:     if (!no_output) {
254:       PetscPrintf(comm,"Iteration %D, function norm = %g\n",i+1,fnorm);
255:     }

257:     /*
258:        Test for convergence
259:      */
260:     if (fnorm <= ttol) {
261:       if (!no_output) {
262:          PetscPrintf(comm,"Converged due to function norm %g < %g (relative tolerance)\n",fnorm,ttol);
263:       }
264:       break;
265:     }
266:     if (ynorm < xtol*(xnorm)) {
267:       if (!no_output) {
268:          PetscPrintf(comm,"Converged due to small update length: %g < %g * %g\n",ynorm,xtol,xnorm);
269:       }
270:       break;
271:     }
272:     if (i > max_functions) {
273:       if (!no_output) {
274:         PetscPrintf(comm,"Exceeded maximum number of function evaluations: %D > %D\n",i,max_functions);
275:       }
276:       break;
277:     }
278:   }
279:   PetscPrintf(comm,"Number of Newton iterations = %D\n",i+1);

281:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
282:      Free work space.  All PETSc objects should be destroyed when they
283:      are no longer needed.
284:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

286:   MatDestroy(J);           VecDestroy(Y);
287:   VecDestroy(user.localX); VecDestroy(X);
288:   VecDestroy(user.localF); VecDestroy(F);
289:   KSPDestroy(ksp);  DADestroy(user.da);
290:   PetscFinalize();

292:   return 0;
293: }
294: /* ------------------------------------------------------------------- */
297: /* 
298:    FormInitialGuess - Forms initial approximation.

300:    Input Parameters:
301:    user - user-defined application context
302:    X - vector

304:    Output Parameter:
305:    X - vector
306:  */
307: PetscErrorCode FormInitialGuess(AppCtx *user,Vec X)
308: {
309:   PetscInt     i,j,row,mx,my,ierr,xs,ys,xm,ym,gxm,gym,gxs,gys;
310:   PetscReal    one = 1.0,lambda,temp1,temp,hx,hy;
311:   PetscScalar  *x;
312:   Vec          localX = user->localX;

314:   mx = user->mx;            my = user->my;            lambda = user->param;
315:   hx = one/(PetscReal)(mx-1);  hy = one/(PetscReal)(my-1);
316:   temp1 = lambda/(lambda + one);

318:   /*
319:      Get a pointer to vector data.
320:        - For default PETSc vectors, VecGetArray() returns a pointer to
321:          the data array.  Otherwise, the routine is implementation dependent.
322:        - You MUST call VecRestoreArray() when you no longer need access to
323:          the array.
324:   */
325:   VecGetArray(localX,&x);

327:   /*
328:      Get local grid boundaries (for 2-dimensional DA):
329:        xs, ys   - starting grid indices (no ghost points)
330:        xm, ym   - widths of local grid (no ghost points)
331:        gxs, gys - starting grid indices (including ghost points)
332:        gxm, gym - widths of local grid (including ghost points)
333:   */
334:   DAGetCorners(user->da,&xs,&ys,PETSC_NULL,&xm,&ym,PETSC_NULL);
335:   DAGetGhostCorners(user->da,&gxs,&gys,PETSC_NULL,&gxm,&gym,PETSC_NULL);

337:   /*
338:      Compute initial guess over the locally owned part of the grid
339:   */
340:   for (j=ys; j<ys+ym; j++) {
341:     temp = (PetscReal)(PetscMin(j,my-j-1))*hy;
342:     for (i=xs; i<xs+xm; i++) {
343:       row = i - gxs + (j - gys)*gxm;
344:       if (i == 0 || j == 0 || i == mx-1 || j == my-1) {
345:         x[row] = 0.0;
346:         continue;
347:       }
348:       x[row] = temp1*sqrt(PetscMin((PetscReal)(PetscMin(i,mx-i-1))*hx,temp));
349:     }
350:   }

352:   /*
353:      Restore vector
354:   */
355:   VecRestoreArray(localX,&x);

357:   /*
358:      Insert values into global vector
359:   */
360:   DALocalToGlobal(user->da,localX,INSERT_VALUES,X);
361:   return 0;
362: }
363: /* ------------------------------------------------------------------- */
366: /* 
367:    ComputeFunction - Evaluates nonlinear function, F(x).

369:    Input Parameters:
370: .  X - input vector
371: .  user - user-defined application context

373:    Output Parameter:
374: .  F - function vector
375:  */
376: PetscErrorCode ComputeFunction(AppCtx *user,Vec X,Vec F)
377: {
379:   PetscInt       i,j,row,mx,my,xs,ys,xm,ym,gxs,gys,gxm,gym;
380:   PetscReal      two = 2.0,one = 1.0,lambda,hx,hy,hxdhy,hydhx,sc;
381:   PetscScalar    u,uxx,uyy,*x,*f;
382:   Vec            localX = user->localX,localF = user->localF;

384:   mx = user->mx;            my = user->my;            lambda = user->param;
385:   hx = one/(PetscReal)(mx-1);  hy = one/(PetscReal)(my-1);
386:   sc = hx*hy*lambda;        hxdhy = hx/hy;            hydhx = hy/hx;

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

397:   /*
398:      Get pointers to vector data
399:   */
400:   VecGetArray(localX,&x);
401:   VecGetArray(localF,&f);

403:   /*
404:      Get local grid boundaries
405:   */
406:   DAGetCorners(user->da,&xs,&ys,PETSC_NULL,&xm,&ym,PETSC_NULL);
407:   DAGetGhostCorners(user->da,&gxs,&gys,PETSC_NULL,&gxm,&gym,PETSC_NULL);

409:   /*
410:      Compute function over the locally owned part of the grid
411:   */
412:   for (j=ys; j<ys+ym; j++) {
413:     row = (j - gys)*gxm + xs - gxs - 1;
414:     for (i=xs; i<xs+xm; i++) {
415:       row++;
416:       if (i == 0 || j == 0 || i == mx-1 || j == my-1) {
417:         f[row] = x[row];
418:         continue;
419:       }
420:       u = x[row];
421:       uxx = (two*u - x[row-1] - x[row+1])*hydhx;
422:       uyy = (two*u - x[row-gxm] - x[row+gxm])*hxdhy;
423:       f[row] = uxx + uyy - sc*PetscExpScalar(u);
424:     }
425:   }

427:   /*
428:      Restore vectors
429:   */
430:   VecRestoreArray(localX,&x);
431:   VecRestoreArray(localF,&f);

433:   /*
434:      Insert values into global vector
435:   */
436:   DALocalToGlobal(user->da,localF,INSERT_VALUES,F);
437:   PetscLogFlops(11*ym*xm);
438:   return 0;
439: }
440: /* ------------------------------------------------------------------- */
443: /*
444:    ComputeJacobian - Evaluates Jacobian matrix.

446:    Input Parameters:
447: .  x - input vector
448: .  user - user-defined application context

450:    Output Parameters:
451: .  jac - Jacobian matrix
452: .  flag - flag indicating matrix structure

454:    Notes:
455:    Due to grid point reordering with DAs, we must always work
456:    with the local grid points, and then transform them to the new
457:    global numbering with the "ltog" mapping (via DAGetGlobalIndices()).
458:    We cannot work directly with the global numbers for the original
459:    uniprocessor grid!
460: */
461: PetscErrorCode ComputeJacobian(AppCtx *user,Vec X,Mat jac,MatStructure *flag)
462: {
464:   Vec            localX = user->localX;   /* local vector */
465:   PetscInt       *ltog;                   /* local-to-global mapping */
466:   PetscInt       i,j,row,mx,my,col[5];
467:   PetscInt       nloc,xs,ys,xm,ym,gxs,gys,gxm,gym,grow;
468:   PetscScalar    two = 2.0,one = 1.0,lambda,v[5],hx,hy,hxdhy,hydhx,sc,*x;

470:   mx = user->mx;            my = user->my;            lambda = user->param;
471:   hx = one/(PetscReal)(mx-1);  hy = one/(PetscReal)(my-1);
472:   sc = hx*hy;               hxdhy = hx/hy;            hydhx = hy/hx;

474:   /*
475:      Scatter ghost points to local vector, using the 2-step process
476:         DAGlobalToLocalBegin(), DAGlobalToLocalEnd().
477:      By placing code between these two statements, computations can be
478:      done while messages are in transition.
479:   */
480:   DAGlobalToLocalBegin(user->da,X,INSERT_VALUES,localX);
481:   DAGlobalToLocalEnd(user->da,X,INSERT_VALUES,localX);

483:   /*
484:      Get pointer to vector data
485:   */
486:   VecGetArray(localX,&x);

488:   /*
489:      Get local grid boundaries
490:   */
491:   DAGetCorners(user->da,&xs,&ys,PETSC_NULL,&xm,&ym,PETSC_NULL);
492:   DAGetGhostCorners(user->da,&gxs,&gys,PETSC_NULL,&gxm,&gym,PETSC_NULL);

494:   /*
495:      Get the global node numbers for all local nodes, including ghost points
496:   */
497:   DAGetGlobalIndices(user->da,&nloc,&ltog);

499:   /* 
500:      Compute entries for the locally owned part of the Jacobian.
501:       - Currently, all PETSc parallel matrix formats are partitioned by
502:         contiguous chunks of rows across the processors. The "grow"
503:         parameter computed below specifies the global row number 
504:         corresponding to each local grid point.
505:       - Each processor needs to insert only elements that it owns
506:         locally (but any non-local elements will be sent to the
507:         appropriate processor during matrix assembly). 
508:       - Always specify global row and columns of matrix entries.
509:       - Here, we set all entries for a particular row at once.
510:   */
511:   for (j=ys; j<ys+ym; j++) {
512:     row = (j - gys)*gxm + xs - gxs - 1;
513:     for (i=xs; i<xs+xm; i++) {
514:       row++;
515:       grow = ltog[row];
516:       /* boundary points */
517:       if (i == 0 || j == 0 || i == mx-1 || j == my-1) {
518:         MatSetValues(jac,1,&grow,1,&grow,&one,INSERT_VALUES);
519:         continue;
520:       }
521:       /* interior grid points */
522:       v[0] = -hxdhy; col[0] = ltog[row - gxm];
523:       v[1] = -hydhx; col[1] = ltog[row - 1];
524:       v[2] = two*(hydhx + hxdhy) - sc*lambda*PetscExpScalar(x[row]); col[2] = grow;
525:       v[3] = -hydhx; col[3] = ltog[row + 1];
526:       v[4] = -hxdhy; col[4] = ltog[row + gxm];
527:       MatSetValues(jac,1,&grow,5,col,v,INSERT_VALUES);
528:     }
529:   }

531:   /* 
532:      Assemble matrix, using the 2-step process:
533:        MatAssemblyBegin(), MatAssemblyEnd().
534:      By placing code between these two statements, computations can be
535:      done while messages are in transition.
536:   */
537:   MatAssemblyBegin(jac,MAT_FINAL_ASSEMBLY);
538:   VecRestoreArray(localX,&x);
539:   MatAssemblyEnd(jac,MAT_FINAL_ASSEMBLY);

541:   /*
542:      Set flag to indicate that the Jacobian matrix retains an identical
543:      nonzero structure throughout all nonlinear iterations (although the
544:      values of the entries change). Thus, we can save some work in setting
545:      up the preconditioner (e.g., no need to redo symbolic factorization for
546:      ILU/ICC preconditioners).
547:       - If the nonzero structure of the matrix is different during
548:         successive linear solves, then the flag DIFFERENT_NONZERO_PATTERN
549:         must be used instead.  If you are unsure whether the matrix
550:         structure has changed or not, use the flag DIFFERENT_NONZERO_PATTERN.
551:       - Caution:  If you specify SAME_NONZERO_PATTERN, PETSc
552:         believes your assertion and does not check the structure
553:         of the matrix.  If you erroneously claim that the structure
554:         is the same when it actually is not, the new preconditioner
555:         will not function correctly.  Thus, use this optimization
556:         feature with caution!
557:   */
558:   *flag = SAME_NONZERO_PATTERN;
559:   return 0;
560: }