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,<og);
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: }