Actual source code: ex1.c

petsc-dev 2014-02-02
Report Typos and Errors
  2: static char help[] = "Solves the nonlinear system, the Bratu (SFI - solid fuel ignition) problem in a 2D rectangular domain.\n\
  3: This example also illustrates the use of matrix coloring.  Runtime options include:\n\
  4:   -par <parameter>, where <parameter> indicates the problem's nonlinearity\n\
  5:      problem SFI:  <parameter> = Bratu parameter (0 <= par <= 6.81)\n\
  6:   -mx <xg>, where <xg> = number of grid points in the x-direction\n\
  7:   -my <yg>, where <yg> = number of grid points in the y-direction\n\n";

  9: /*T
 10:    Concepts: SNES^sequential Bratu example
 11:    Processors: 1
 12: T*/

 14: /* ------------------------------------------------------------------------

 16:     Solid Fuel Ignition (SFI) problem.  This problem is modeled by
 17:     the partial differential equation

 19:             -Laplacian u - lambda*exp(u) = 0,  0 < x,y < 1,

 21:     with boundary conditions

 23:              u = 0  for  x = 0, x = 1, y = 0, y = 1.

 25:     A finite difference approximation with the usual 5-point stencil
 26:     is used to discretize the boundary value problem to obtain a nonlinear
 27:     system of equations.

 29:     The parallel version of this code is snes/examples/tutorials/ex5.c

 31:   ------------------------------------------------------------------------- */

 33: /*
 34:    Include "petscsnes.h" so that we can use SNES solvers.  Note that
 35:    this file automatically includes:
 36:      petscsys.h       - base PETSc routines   petscvec.h - vectors
 37:      petscmat.h - matrices
 38:      petscis.h     - index sets            petscksp.h - Krylov subspace methods
 39:      petscviewer.h - viewers               petscpc.h  - preconditioners
 40:      petscksp.h   - linear solvers
 41: */

 43: #include <petscsnes.h>

 45: /*
 46:    User-defined application context - contains data needed by the
 47:    application-provided call-back routines, FormJacobian() and
 48:    FormFunction().
 49: */
 50: typedef struct {
 51:   PetscReal param;              /* test problem parameter */
 52:   PetscInt  mx;                 /* Discretization in x-direction */
 53:   PetscInt  my;                 /* Discretization in y-direction */
 54: } AppCtx;

 56: /*
 57:    User-defined routines
 58: */
 59: extern PetscErrorCode FormJacobian(SNES,Vec,Mat*,Mat*,MatStructure*,void*);
 60: extern PetscErrorCode FormFunction(SNES,Vec,Vec,void*);
 61: extern PetscErrorCode FormInitialGuess(AppCtx*,Vec);

 65: int main(int argc,char **argv)
 66: {
 67:   SNES           snes;                 /* nonlinear solver context */
 68:   Vec            x,r;                 /* solution, residual vectors */
 69:   Mat            J;                    /* Jacobian matrix */
 70:   AppCtx         user;                 /* user-defined application context */
 72:   PetscInt       i,its,N,hist_its[50];
 73:   PetscMPIInt    size;
 74:   PetscReal      bratu_lambda_max = 6.81,bratu_lambda_min = 0.,history[50];
 75:   MatFDColoring  fdcoloring;
 76:   PetscBool      matrix_free = PETSC_FALSE,flg,fd_coloring = PETSC_FALSE;

 78:   PetscInitialize(&argc,&argv,(char*)0,help);
 79:   MPI_Comm_size(PETSC_COMM_WORLD,&size);
 80:   if (size != 1) SETERRQ(PETSC_COMM_SELF,1,"This is a uniprocessor example only!");

 82:   /*
 83:      Initialize problem parameters
 84:   */
 85:   user.mx = 4; user.my = 4; user.param = 6.0;
 86:   PetscOptionsGetInt(NULL,"-mx",&user.mx,NULL);
 87:   PetscOptionsGetInt(NULL,"-my",&user.my,NULL);
 88:   PetscOptionsGetReal(NULL,"-par",&user.param,NULL);
 89:   if (user.param >= bratu_lambda_max || user.param <= bratu_lambda_min) SETERRQ(PETSC_COMM_SELF,1,"Lambda is out of range");
 90:   N = user.mx*user.my;

 92:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 93:      Create nonlinear solver context
 94:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

 96:   SNESCreate(PETSC_COMM_WORLD,&snes);

 98:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 99:      Create vector data structures; set function evaluation routine
100:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

102:   VecCreate(PETSC_COMM_WORLD,&x);
103:   VecSetSizes(x,PETSC_DECIDE,N);
104:   VecSetFromOptions(x);
105:   VecDuplicate(x,&r);

107:   /*
108:      Set function evaluation routine and vector.  Whenever the nonlinear
109:      solver needs to evaluate the nonlinear function, it will call this
110:      routine.
111:       - Note that the final routine argument is the user-defined
112:         context that provides application-specific data for the
113:         function evaluation routine.
114:   */
115:   SNESSetFunction(snes,r,FormFunction,(void*)&user);

117:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
118:      Create matrix data structure; set Jacobian evaluation routine
119:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

121:   /*
122:      Create matrix. Here we only approximately preallocate storage space
123:      for the Jacobian.  See the users manual for a discussion of better
124:      techniques for preallocating matrix memory.
125:   */
126:   PetscOptionsGetBool(NULL,"-snes_mf",&matrix_free,NULL);
127:   if (!matrix_free) {
128:     PetscBool matrix_free_operator = PETSC_FALSE;
129:     PetscOptionsGetBool(NULL,"-snes_mf_operator",&matrix_free_operator,NULL);
130:     if (matrix_free_operator) matrix_free = PETSC_FALSE;
131:   }
132:   if (!matrix_free) {
133:     MatCreateSeqAIJ(PETSC_COMM_WORLD,N,N,5,NULL,&J);
134:   }

136:   /*
137:      This option will cause the Jacobian to be computed via finite differences
138:     efficiently using a coloring of the columns of the matrix.
139:   */
140:   PetscOptionsGetBool(NULL,"-snes_fd_coloring",&fd_coloring,NULL);
141:   if (matrix_free && fd_coloring) SETERRQ(PETSC_COMM_SELF,1,"Use only one of -snes_mf, -snes_fd_coloring options!\nYou can do -snes_mf_operator -snes_fd_coloring");

143:   if (fd_coloring) {
144:     ISColoring   iscoloring;
145:     MatColoring  mc;
146:     MatStructure str;

148:     /*
149:       This initializes the nonzero structure of the Jacobian. This is artificial
150:       because clearly if we had a routine to compute the Jacobian we won't need
151:       to use finite differences.
152:     */
153:     FormJacobian(snes,x,&J,&J,&str,&user);

155:     /*
156:        Color the matrix, i.e. determine groups of columns that share no common
157:       rows. These columns in the Jacobian can all be computed simulataneously.
158:     */
159:     MatColoringCreate(J,&mc);
160:     MatColoringSetType(mc,MATCOLORINGSL);
161:     MatColoringSetFromOptions(mc);
162:     MatColoringApply(mc,&iscoloring);
163:     MatColoringDestroy(&mc);
164:     /*
165:        Create the data structure that SNESComputeJacobianDefaultColor() uses
166:        to compute the actual Jacobians via finite differences.
167:     */
168:     MatFDColoringCreate(J,iscoloring,&fdcoloring);
169:     MatFDColoringSetFunction(fdcoloring,(PetscErrorCode (*)(void))FormFunction,&user);
170:     MatFDColoringSetFromOptions(fdcoloring);
171:     MatFDColoringSetUp(J,iscoloring,fdcoloring);
172:     /*
173:         Tell SNES to use the routine SNESComputeJacobianDefaultColor()
174:       to compute Jacobians.
175:     */
176:     SNESSetJacobian(snes,J,J,SNESComputeJacobianDefaultColor,fdcoloring);
177:     ISColoringDestroy(&iscoloring);
178:   }
179:   /*
180:      Set Jacobian matrix data structure and default Jacobian evaluation
181:      routine.  Whenever the nonlinear solver needs to compute the
182:      Jacobian matrix, it will call this routine.
183:       - Note that the final routine argument is the user-defined
184:         context that provides application-specific data for the
185:         Jacobian evaluation routine.
186:       - The user can override with:
187:          -snes_fd : default finite differencing approximation of Jacobian
188:          -snes_mf : matrix-free Newton-Krylov method with no preconditioning
189:                     (unless user explicitly sets preconditioner)
190:          -snes_mf_operator : form preconditioning matrix as set by the user,
191:                              but use matrix-free approx for Jacobian-vector
192:                              products within Newton-Krylov method
193:   */
194:   else if (!matrix_free) {
195:     SNESSetJacobian(snes,J,J,FormJacobian,(void*)&user);
196:   }

198:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
199:      Customize nonlinear solver; set runtime options
200:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

202:   /*
203:      Set runtime options (e.g., -snes_monitor -snes_rtol <rtol> -ksp_type <type>)
204:   */
205:   SNESSetFromOptions(snes);

207:   /*
208:      Set array that saves the function norms.  This array is intended
209:      when the user wants to save the convergence history for later use
210:      rather than just to view the function norms via -snes_monitor.
211:   */
212:   SNESSetConvergenceHistory(snes,history,hist_its,50,PETSC_TRUE);

214:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
215:      Evaluate initial guess; then solve nonlinear system
216:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
217:   /*
218:      Note: The user should initialize the vector, x, with the initial guess
219:      for the nonlinear solver prior to calling SNESSolve().  In particular,
220:      to employ an initial guess of zero, the user should explicitly set
221:      this vector to zero by calling VecSet().
222:   */
223:   FormInitialGuess(&user,x);
224:   SNESSolve(snes,NULL,x);
225:   SNESGetIterationNumber(snes,&its);
226:   PetscPrintf(PETSC_COMM_WORLD,"Number of SNES iterations = %D\n",its);


229:   /*
230:      Print the convergence history.  This is intended just to demonstrate
231:      use of the data attained via SNESSetConvergenceHistory().
232:   */
233:   PetscOptionsHasName(NULL,"-print_history",&flg);
234:   if (flg) {
235:     for (i=0; i<its+1; i++) {
236:       PetscPrintf(PETSC_COMM_WORLD,"iteration %D: Linear iterations %D Function norm = %g\n",i,hist_its[i],(double)history[i]);
237:     }
238:   }

240:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
241:      Free work space.  All PETSc objects should be destroyed when they
242:      are no longer needed.
243:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

245:   if (!matrix_free) {
246:     MatDestroy(&J);
247:   }
248:   if (fd_coloring) {
249:     MatFDColoringDestroy(&fdcoloring);
250:   }
251:   VecDestroy(&x);
252:   VecDestroy(&r);
253:   SNESDestroy(&snes);
254:   PetscFinalize();

256:   return 0;
257: }
258: /* ------------------------------------------------------------------- */
261: /*
262:    FormInitialGuess - Forms initial approximation.

264:    Input Parameters:
265:    user - user-defined application context
266:    X - vector

268:    Output Parameter:
269:    X - vector
270:  */
271: PetscErrorCode FormInitialGuess(AppCtx *user,Vec X)
272: {
273:   PetscInt       i,j,row,mx,my;
275:   PetscReal      lambda,temp1,temp,hx,hy;
276:   PetscScalar    *x;

278:   mx     = user->mx;
279:   my     = user->my;
280:   lambda = user->param;

282:   hx = 1.0 / (PetscReal)(mx-1);
283:   hy = 1.0 / (PetscReal)(my-1);

285:   /*
286:      Get a pointer to vector data.
287:        - For default PETSc vectors, VecGetArray() returns a pointer to
288:          the data array.  Otherwise, the routine is implementation dependent.
289:        - You MUST call VecRestoreArray() when you no longer need access to
290:          the array.
291:   */
292:   VecGetArray(X,&x);
293:   temp1 = lambda/(lambda + 1.0);
294:   for (j=0; j<my; j++) {
295:     temp = (PetscReal)(PetscMin(j,my-j-1))*hy;
296:     for (i=0; i<mx; i++) {
297:       row = i + j*mx;
298:       if (i == 0 || j == 0 || i == mx-1 || j == my-1) {
299:         x[row] = 0.0;
300:         continue;
301:       }
302:       x[row] = temp1*PetscSqrtReal(PetscMin((PetscReal)(PetscMin(i,mx-i-1))*hx,temp));
303:     }
304:   }

306:   /*
307:      Restore vector
308:   */
309:   VecRestoreArray(X,&x);
310:   return 0;
311: }
312: /* ------------------------------------------------------------------- */
315: /*
316:    FormFunction - Evaluates nonlinear function, F(x).

318:    Input Parameters:
319: .  snes - the SNES context
320: .  X - input vector
321: .  ptr - optional user-defined context, as set by SNESSetFunction()

323:    Output Parameter:
324: .  F - function vector
325:  */
326: PetscErrorCode FormFunction(SNES snes,Vec X,Vec F,void *ptr)
327: {
328:   AppCtx         *user = (AppCtx*)ptr;
329:   PetscInt       i,j,row,mx,my;
331:   PetscReal      two = 2.0,one = 1.0,lambda,hx,hy,hxdhy,hydhx;
332:   PetscScalar    ut,ub,ul,ur,u,uxx,uyy,sc,*x,*f;

334:   mx     = user->mx;
335:   my     = user->my;
336:   lambda = user->param;
337:   hx     = one / (PetscReal)(mx-1);
338:   hy     = one / (PetscReal)(my-1);
339:   sc     = hx*hy;
340:   hxdhy  = hx/hy;
341:   hydhx  = hy/hx;

343:   /*
344:      Get pointers to vector data
345:   */
346:   VecGetArray(X,&x);
347:   VecGetArray(F,&f);

349:   /*
350:      Compute function
351:   */
352:   for (j=0; j<my; j++) {
353:     for (i=0; i<mx; i++) {
354:       row = i + j*mx;
355:       if (i == 0 || j == 0 || i == mx-1 || j == my-1) {
356:         f[row] = x[row];
357:         continue;
358:       }
359:       u      = x[row];
360:       ub     = x[row - mx];
361:       ul     = x[row - 1];
362:       ut     = x[row + mx];
363:       ur     = x[row + 1];
364:       uxx    = (-ur + two*u - ul)*hydhx;
365:       uyy    = (-ut + two*u - ub)*hxdhy;
366:       f[row] = uxx + uyy - sc*lambda*PetscExpScalar(u);
367:     }
368:   }

370:   /*
371:      Restore vectors
372:   */
373:   VecRestoreArray(X,&x);
374:   VecRestoreArray(F,&f);
375:   return 0;
376: }
377: /* ------------------------------------------------------------------- */
380: /*
381:    FormJacobian - Evaluates Jacobian matrix.

383:    Input Parameters:
384: .  snes - the SNES context
385: .  x - input vector
386: .  ptr - optional user-defined context, as set by SNESSetJacobian()

388:    Output Parameters:
389: .  A - Jacobian matrix
390: .  B - optionally different preconditioning matrix
391: .  flag - flag indicating matrix structure
392: */
393: PetscErrorCode FormJacobian(SNES snes,Vec X,Mat *J,Mat *B,MatStructure *flag,void *ptr)
394: {
395:   AppCtx         *user = (AppCtx*)ptr;   /* user-defined applicatin context */
396:   Mat            jac   = *B;             /* Jacobian matrix */
397:   PetscInt       i,j,row,mx,my,col[5];
399:   PetscScalar    two = 2.0,one = 1.0,lambda,v[5],sc,*x;
400:   PetscReal      hx,hy,hxdhy,hydhx;

402:   mx     = user->mx;
403:   my     = user->my;
404:   lambda = user->param;
405:   hx     = 1.0 / (PetscReal)(mx-1);
406:   hy     = 1.0 / (PetscReal)(my-1);
407:   sc     = hx*hy;
408:   hxdhy  = hx/hy;
409:   hydhx  = hy/hx;

411:   /*
412:      Get pointer to vector data
413:   */
414:   VecGetArray(X,&x);

416:   /*
417:      Compute entries of the Jacobian
418:   */
419:   for (j=0; j<my; j++) {
420:     for (i=0; i<mx; i++) {
421:       row = i + j*mx;
422:       if (i == 0 || j == 0 || i == mx-1 || j == my-1) {
423:         MatSetValues(jac,1,&row,1,&row,&one,INSERT_VALUES);
424:         continue;
425:       }
426:       v[0] = -hxdhy; col[0] = row - mx;
427:       v[1] = -hydhx; col[1] = row - 1;
428:       v[2] = two*(hydhx + hxdhy) - sc*lambda*PetscExpScalar(x[row]); col[2] = row;
429:       v[3] = -hydhx; col[3] = row + 1;
430:       v[4] = -hxdhy; col[4] = row + mx;
431:       MatSetValues(jac,1,&row,5,col,v,INSERT_VALUES);
432:     }
433:   }

435:   /*
436:      Restore vector
437:   */
438:   VecRestoreArray(X,&x);

440:   /*
441:      Assemble matrix
442:   */
443:   MatAssemblyBegin(jac,MAT_FINAL_ASSEMBLY);
444:   MatAssemblyEnd(jac,MAT_FINAL_ASSEMBLY);

446:   if (jac != *J) {
447:     MatAssemblyBegin(*J,MAT_FINAL_ASSEMBLY);
448:     MatAssemblyEnd(*J,MAT_FINAL_ASSEMBLY);
449:   }

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