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