Actual source code: ex14.c
petsc-dev 2014-02-02
2: static char help[] = "Bratu nonlinear PDE in 3d.\n\
3: We solve the Bratu (SFI - solid fuel ignition) problem in a 3D rectangular\n\
4: domain, using distributed arrays (DMDAs) to partition the parallel grid.\n\
5: The command line options include:\n\
6: -par <parameter>, where <parameter> indicates the problem's nonlinearity\n\
7: problem SFI: <parameter> = Bratu parameter (0 <= par <= 6.81)\n\n";
9: /*T
10: Concepts: SNES^parallel Bratu example
11: Concepts: DMDA^using distributed arrays;
12: Processors: n
13: T*/
15: /* ------------------------------------------------------------------------
17: Solid Fuel Ignition (SFI) problem. This problem is modeled by
18: the partial differential equation
20: -Laplacian u - lambda*exp(u) = 0, 0 < x,y < 1,
22: with boundary conditions
24: u = 0 for x = 0, x = 1, y = 0, y = 1, z = 0, z = 1
26: A finite difference approximation with the usual 7-point stencil
27: is used to discretize the boundary value problem to obtain a nonlinear
28: system of equations.
31: ------------------------------------------------------------------------- */
33: /*
34: Include "petscdmda.h" so that we can use distributed arrays (DMDAs).
35: Include "petscsnes.h" so that we can use SNES solvers. Note that this
36: file automatically includes:
37: petscsys.h - base PETSc routines petscvec.h - vectors
38: petscmat.h - matrices
39: petscis.h - index sets petscksp.h - Krylov subspace methods
40: petscviewer.h - viewers petscpc.h - preconditioners
41: petscksp.h - linear solvers
42: */
43: #include <petscdmda.h>
44: #include <petscsnes.h>
47: /*
48: User-defined application context - contains data needed by the
49: application-provided call-back routines, FormJacobian() and
50: FormFunction().
51: */
52: typedef struct {
53: PetscReal param; /* test problem parameter */
54: DM da; /* distributed array data structure */
55: } AppCtx;
57: /*
58: User-defined routines
59: */
60: extern PetscErrorCode FormFunction(SNES,Vec,Vec,void*),FormInitialGuess(AppCtx*,Vec);
61: extern PetscErrorCode 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: PetscInt its; /* iterations for convergence */
72: MatFDColoring matfdcoloring;
73: PetscBool matrix_free = PETSC_FALSE,coloring = PETSC_FALSE;
75: PetscReal bratu_lambda_max = 6.81,bratu_lambda_min = 0.,fnorm;
77: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
78: Initialize program
79: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
81: PetscInitialize(&argc,&argv,(char*)0,help);
83: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
84: Initialize problem parameters
85: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
86: user.param = 6.0;
87: PetscOptionsGetReal(NULL,"-par",&user.param,NULL);
88: if (user.param >= bratu_lambda_max || user.param <= bratu_lambda_min) SETERRQ(PETSC_COMM_SELF,1,"Lambda is out of range");
90: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
91: Create nonlinear solver context
92: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
93: SNESCreate(PETSC_COMM_WORLD,&snes);
95: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
96: Create distributed array (DMDA) to manage parallel grid and vectors
97: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
98: DMDACreate3d(PETSC_COMM_WORLD,DMDA_BOUNDARY_NONE,DMDA_BOUNDARY_NONE,DMDA_BOUNDARY_NONE,DMDA_STENCIL_STAR,-4,-4,-4,PETSC_DECIDE,PETSC_DECIDE,
99: PETSC_DECIDE,1,1,NULL,NULL,NULL,&user.da);
101: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
102: Extract global vectors from DMDA; then duplicate for remaining
103: vectors that are the same types
104: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
105: DMCreateGlobalVector(user.da,&x);
106: VecDuplicate(x,&r);
108: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
109: Set function evaluation routine and vector
110: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
111: SNESSetFunction(snes,r,FormFunction,(void*)&user);
113: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
114: Create matrix data structure; set Jacobian evaluation routine
116: Set Jacobian matrix data structure and default Jacobian evaluation
117: routine. User can override with:
118: -snes_mf : matrix-free Newton-Krylov method with no preconditioning
119: (unless user explicitly sets preconditioner)
120: -snes_mf_operator : form preconditioning matrix as set by the user,
121: but use matrix-free approx for Jacobian-vector
122: products within Newton-Krylov method
123: -fdcoloring : using finite differences with coloring to compute the Jacobian
125: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
126: PetscOptionsGetBool(NULL,"-snes_mf",&matrix_free,NULL);
127: PetscOptionsGetBool(NULL,"-fdcoloring",&coloring,NULL);
128: if (!matrix_free) {
129: DMSetMatType(user.da,MATAIJ);
130: DMCreateMatrix(user.da,&J);
131: if (coloring) {
132: ISColoring iscoloring;
133: DMCreateColoring(user.da,IS_COLORING_GLOBAL,&iscoloring);
134: MatFDColoringCreate(J,iscoloring,&matfdcoloring);
135: MatFDColoringSetFunction(matfdcoloring,(PetscErrorCode (*)(void))FormFunction,&user);
136: MatFDColoringSetFromOptions(matfdcoloring);
137: MatFDColoringSetUp(J,iscoloring,matfdcoloring);
138: SNESSetJacobian(snes,J,J,SNESComputeJacobianDefaultColor,matfdcoloring);
139: ISColoringDestroy(&iscoloring);
140: } else {
141: SNESSetJacobian(snes,J,J,FormJacobian,&user);
142: }
143: }
145: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
146: Customize nonlinear solver; set runtime options
147: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
148: SNESSetDM(snes,user.da);
149: SNESSetFromOptions(snes);
151: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
152: Evaluate initial guess
153: Note: The user should initialize the vector, x, with the initial guess
154: for the nonlinear solver prior to calling SNESSolve(). In particular,
155: to employ an initial guess of zero, the user should explicitly set
156: this vector to zero by calling VecSet().
157: */
158: FormInitialGuess(&user,x);
160: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
161: Solve nonlinear system
162: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
163: SNESSolve(snes,NULL,x);
164: SNESGetIterationNumber(snes,&its);
166: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
167: Explicitly check norm of the residual of the solution
168: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
169: FormFunction(snes,x,r,(void*)&user);
170: VecNorm(r,NORM_2,&fnorm);
171: PetscPrintf(PETSC_COMM_WORLD,"Number of SNES iterations = %D fnorm %g\n",its,(double)fnorm);
173: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
174: Free work space. All PETSc objects should be destroyed when they
175: are no longer needed.
176: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
178: if (!matrix_free) {
179: MatDestroy(&J);
180: }
181: VecDestroy(&x);
182: VecDestroy(&r);
183: SNESDestroy(&snes);
184: DMDestroy(&user.da);
185: if (coloring) {MatFDColoringDestroy(&matfdcoloring);}
186: PetscFinalize();
187: return(0);
188: }
189: /* ------------------------------------------------------------------- */
192: /*
193: FormInitialGuess - Forms initial approximation.
195: Input Parameters:
196: user - user-defined application context
197: X - vector
199: Output Parameter:
200: X - vector
201: */
202: PetscErrorCode FormInitialGuess(AppCtx *user,Vec X)
203: {
204: PetscInt i,j,k,Mx,My,Mz,xs,ys,zs,xm,ym,zm;
206: PetscReal lambda,temp1,hx,hy,hz,tempk,tempj;
207: PetscScalar ***x;
210: DMDAGetInfo(user->da,PETSC_IGNORE,&Mx,&My,&Mz,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE);
212: lambda = user->param;
213: hx = 1.0/(PetscReal)(Mx-1);
214: hy = 1.0/(PetscReal)(My-1);
215: hz = 1.0/(PetscReal)(Mz-1);
216: temp1 = lambda/(lambda + 1.0);
218: /*
219: Get a pointer to vector data.
220: - For default PETSc vectors, VecGetArray() returns a pointer to
221: the data array. Otherwise, the routine is implementation dependent.
222: - You MUST call VecRestoreArray() when you no longer need access to
223: the array.
224: */
225: DMDAVecGetArray(user->da,X,&x);
227: /*
228: Get local grid boundaries (for 3-dimensional DMDA):
229: xs, ys, zs - starting grid indices (no ghost points)
230: xm, ym, zm - widths of local grid (no ghost points)
232: */
233: DMDAGetCorners(user->da,&xs,&ys,&zs,&xm,&ym,&zm);
235: /*
236: Compute initial guess over the locally owned part of the grid
237: */
238: for (k=zs; k<zs+zm; k++) {
239: tempk = (PetscReal)(PetscMin(k,Mz-k-1))*hz;
240: for (j=ys; j<ys+ym; j++) {
241: tempj = PetscMin((PetscReal)(PetscMin(j,My-j-1))*hy,tempk);
242: for (i=xs; i<xs+xm; i++) {
243: if (i == 0 || j == 0 || k == 0 || i == Mx-1 || j == My-1 || k == Mz-1) {
244: /* boundary conditions are all zero Dirichlet */
245: x[k][j][i] = 0.0;
246: } else {
247: x[k][j][i] = temp1*PetscSqrtReal(PetscMin((PetscReal)(PetscMin(i,Mx-i-1))*hx,tempj));
248: }
249: }
250: }
251: }
253: /*
254: Restore vector
255: */
256: DMDAVecRestoreArray(user->da,X,&x);
257: return(0);
258: }
259: /* ------------------------------------------------------------------- */
262: /*
263: FormFunction - Evaluates nonlinear function, F(x).
265: Input Parameters:
266: . snes - the SNES context
267: . X - input vector
268: . ptr - optional user-defined context, as set by SNESSetFunction()
270: Output Parameter:
271: . F - function vector
272: */
273: PetscErrorCode FormFunction(SNES snes,Vec X,Vec F,void *ptr)
274: {
275: AppCtx *user = (AppCtx*)ptr;
277: PetscInt i,j,k,Mx,My,Mz,xs,ys,zs,xm,ym,zm;
278: PetscReal two = 2.0,lambda,hx,hy,hz,hxhzdhy,hyhzdhx,hxhydhz,sc;
279: PetscScalar u_north,u_south,u_east,u_west,u_up,u_down,u,u_xx,u_yy,u_zz,***x,***f;
280: Vec localX;
281: DM da;
284: SNESGetDM(snes,&da);
285: DMGetLocalVector(da,&localX);
286: DMDAGetInfo(da,PETSC_IGNORE,&Mx,&My,&Mz,PETSC_IGNORE,PETSC_IGNORE,
287: PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE);
289: lambda = user->param;
290: hx = 1.0/(PetscReal)(Mx-1);
291: hy = 1.0/(PetscReal)(My-1);
292: hz = 1.0/(PetscReal)(Mz-1);
293: sc = hx*hy*hz*lambda;
294: hxhzdhy = hx*hz/hy;
295: hyhzdhx = hy*hz/hx;
296: hxhydhz = hx*hy/hz;
298: /*
299: Scatter ghost points to local vector,using the 2-step process
300: DMGlobalToLocalBegin(),DMGlobalToLocalEnd().
301: By placing code between these two statements, computations can be
302: done while messages are in transition.
303: */
304: DMGlobalToLocalBegin(da,X,INSERT_VALUES,localX);
305: DMGlobalToLocalEnd(da,X,INSERT_VALUES,localX);
307: /*
308: Get pointers to vector data
309: */
310: DMDAVecGetArray(da,localX,&x);
311: DMDAVecGetArray(da,F,&f);
313: /*
314: Get local grid boundaries
315: */
316: DMDAGetCorners(da,&xs,&ys,&zs,&xm,&ym,&zm);
318: /*
319: Compute function over the locally owned part of the grid
320: */
321: for (k=zs; k<zs+zm; k++) {
322: for (j=ys; j<ys+ym; j++) {
323: for (i=xs; i<xs+xm; i++) {
324: if (i == 0 || j == 0 || k == 0 || i == Mx-1 || j == My-1 || k == Mz-1) {
325: f[k][j][i] = x[k][j][i];
326: } else {
327: u = x[k][j][i];
328: u_east = x[k][j][i+1];
329: u_west = x[k][j][i-1];
330: u_north = x[k][j+1][i];
331: u_south = x[k][j-1][i];
332: u_up = x[k+1][j][i];
333: u_down = x[k-1][j][i];
334: u_xx = (-u_east + two*u - u_west)*hyhzdhx;
335: u_yy = (-u_north + two*u - u_south)*hxhzdhy;
336: u_zz = (-u_up + two*u - u_down)*hxhydhz;
337: f[k][j][i] = u_xx + u_yy + u_zz - sc*PetscExpScalar(u);
338: }
339: }
340: }
341: }
343: /*
344: Restore vectors
345: */
346: DMDAVecRestoreArray(da,localX,&x);
347: DMDAVecRestoreArray(da,F,&f);
348: DMRestoreLocalVector(da,&localX);
349: PetscLogFlops(11.0*ym*xm);
350: return(0);
351: }
352: /* ------------------------------------------------------------------- */
355: /*
356: FormJacobian - Evaluates Jacobian matrix.
358: Input Parameters:
359: . snes - the SNES context
360: . x - input vector
361: . ptr - optional user-defined context, as set by SNESSetJacobian()
363: Output Parameters:
364: . A - Jacobian matrix
365: . B - optionally different preconditioning matrix
366: . flag - flag indicating matrix structure
368: */
369: PetscErrorCode FormJacobian(SNES snes,Vec X,Mat *J,Mat *B,MatStructure *flag,void *ptr)
370: {
371: AppCtx *user = (AppCtx*)ptr; /* user-defined application context */
372: Mat jac = *B; /* Jacobian matrix */
373: Vec localX;
375: PetscInt i,j,k,Mx,My,Mz;
376: MatStencil col[7],row;
377: PetscInt xs,ys,zs,xm,ym,zm;
378: PetscScalar lambda,v[7],hx,hy,hz,hxhzdhy,hyhzdhx,hxhydhz,sc,***x;
379: DM da;
382: SNESGetDM(snes,&da);
383: DMGetLocalVector(da,&localX);
384: DMDAGetInfo(da,PETSC_IGNORE,&Mx,&My,&Mz,PETSC_IGNORE,PETSC_IGNORE,
385: PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE);
387: lambda = user->param;
388: hx = 1.0/(PetscReal)(Mx-1);
389: hy = 1.0/(PetscReal)(My-1);
390: hz = 1.0/(PetscReal)(Mz-1);
391: sc = hx*hy*hz*lambda;
392: hxhzdhy = hx*hz/hy;
393: hyhzdhx = hy*hz/hx;
394: hxhydhz = hx*hy/hz;
396: /*
397: Scatter ghost points to local vector, using the 2-step process
398: DMGlobalToLocalBegin(), DMGlobalToLocalEnd().
399: By placing code between these two statements, computations can be
400: done while messages are in transition.
401: */
402: DMGlobalToLocalBegin(da,X,INSERT_VALUES,localX);
403: DMGlobalToLocalEnd(da,X,INSERT_VALUES,localX);
405: /*
406: Get pointer to vector data
407: */
408: DMDAVecGetArray(da,localX,&x);
410: /*
411: Get local grid boundaries
412: */
413: DMDAGetCorners(da,&xs,&ys,&zs,&xm,&ym,&zm);
415: /*
416: Compute entries for the locally owned part of the Jacobian.
417: - Currently, all PETSc parallel matrix formats are partitioned by
418: contiguous chunks of rows across the processors.
419: - Each processor needs to insert only elements that it owns
420: locally (but any non-local elements will be sent to the
421: appropriate processor during matrix assembly).
422: - Here, we set all entries for a particular row at once.
423: - We can set matrix entries either using either
424: MatSetValuesLocal() or MatSetValues(), as discussed above.
425: */
426: for (k=zs; k<zs+zm; k++) {
427: for (j=ys; j<ys+ym; j++) {
428: for (i=xs; i<xs+xm; i++) {
429: row.k = k; row.j = j; row.i = i;
430: /* boundary points */
431: if (i == 0 || j == 0 || k == 0|| i == Mx-1 || j == My-1 || k == Mz-1) {
432: v[0] = 1.0;
433: MatSetValuesStencil(jac,1,&row,1,&row,v,INSERT_VALUES);
434: } else {
435: /* interior grid points */
436: v[0] = -hxhydhz; col[0].k=k-1;col[0].j=j; col[0].i = i;
437: v[1] = -hxhzdhy; col[1].k=k; col[1].j=j-1;col[1].i = i;
438: v[2] = -hyhzdhx; col[2].k=k; col[2].j=j; col[2].i = i-1;
439: v[3] = 2.0*(hyhzdhx+hxhzdhy+hxhydhz)-sc*PetscExpScalar(x[k][j][i]);col[3].k=row.k;col[3].j=row.j;col[3].i = row.i;
440: v[4] = -hyhzdhx; col[4].k=k; col[4].j=j; col[4].i = i+1;
441: v[5] = -hxhzdhy; col[5].k=k; col[5].j=j+1;col[5].i = i;
442: v[6] = -hxhydhz; col[6].k=k+1;col[6].j=j; col[6].i = i;
443: MatSetValuesStencil(jac,1,&row,7,col,v,INSERT_VALUES);
444: }
445: }
446: }
447: }
448: DMDAVecRestoreArray(da,localX,&x);
449: DMRestoreLocalVector(da,&localX);
451: /*
452: Assemble matrix, using the 2-step process:
453: MatAssemblyBegin(), MatAssemblyEnd().
454: */
455: MatAssemblyBegin(jac,MAT_FINAL_ASSEMBLY);
456: MatAssemblyEnd(jac,MAT_FINAL_ASSEMBLY);
458: /*
459: Normally since the matrix has already been assembled above; this
460: would do nothing. But in the matrix free mode -snes_mf_operator
461: this tells the "matrix-free" matrix that a new linear system solve
462: is about to be done.
463: */
465: MatAssemblyBegin(*J,MAT_FINAL_ASSEMBLY);
466: MatAssemblyEnd(*J,MAT_FINAL_ASSEMBLY);
468: /*
469: Set flag to indicate that the Jacobian matrix retains an identical
470: nonzero structure throughout all nonlinear iterations (although the
471: values of the entries change). Thus, we can save some work in setting
472: up the preconditioner (e.g., no need to redo symbolic factorization for
473: ILU/ICC preconditioners).
474: - If the nonzero structure of the matrix is different during
475: successive linear solves, then the flag DIFFERENT_NONZERO_PATTERN
476: must be used instead. If you are unsure whether the matrix
477: structure has changed or not, use the flag DIFFERENT_NONZERO_PATTERN.
478: - Caution: If you specify SAME_NONZERO_PATTERN, PETSc
479: believes your assertion and does not check the structure
480: of the matrix. If you erroneously claim that the structure
481: is the same when it actually is not, the new preconditioner
482: will not function correctly. Thus, use this optimization
483: feature with caution!
484: */
485: *flag = SAME_NONZERO_PATTERN;
488: /*
489: Tell the matrix we will never add a new nonzero location to the
490: matrix. If we do, it will generate an error.
491: */
492: MatSetOption(jac,MAT_NEW_NONZERO_LOCATION_ERR,PETSC_TRUE);
493: return(0);
494: }