Actual source code: ex14.c
petsc-3.13.6 2020-09-29
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*/
17: /* ------------------------------------------------------------------------
19: Solid Fuel Ignition (SFI) problem. This problem is modeled by
20: the partial differential equation
22: -Laplacian u - lambda*exp(u) = 0, 0 < x,y < 1,
24: with boundary conditions
26: u = 0 for x = 0, x = 1, y = 0, y = 1, z = 0, z = 1
28: A finite difference approximation with the usual 7-point stencil
29: is used to discretize the boundary value problem to obtain a nonlinear
30: system of equations.
33: ------------------------------------------------------------------------- */
35: /*
36: Include "petscdmda.h" so that we can use distributed arrays (DMDAs).
37: Include "petscsnes.h" so that we can use SNES solvers. Note that this
38: file automatically includes:
39: petscsys.h - base PETSc routines petscvec.h - vectors
40: petscmat.h - matrices
41: petscis.h - index sets petscksp.h - Krylov subspace methods
42: petscviewer.h - viewers petscpc.h - preconditioners
43: petscksp.h - linear solvers
44: */
45: #include <petscdm.h>
46: #include <petscdmda.h>
47: #include <petscsnes.h>
50: /*
51: User-defined Section 1.5 Writing Application Codes with PETSc context - contains data needed by the
52: Section 1.5 Writing Application Codes with PETSc-provided call-back routines, FormJacobian() and
53: FormFunction().
54: */
55: typedef struct {
56: PetscReal param; /* test problem parameter */
57: DM da; /* distributed array data structure */
58: } AppCtx;
60: /*
61: User-defined routines
62: */
63: extern PetscErrorCode FormFunctionLocal(SNES,Vec,Vec,void*);
64: extern PetscErrorCode FormFunction(SNES,Vec,Vec,void*);
65: extern PetscErrorCode FormInitialGuess(AppCtx*,Vec);
66: extern PetscErrorCode FormJacobian(SNES,Vec,Mat,Mat,void*);
68: int main(int argc,char **argv)
69: {
70: SNES snes; /* nonlinear solver */
71: Vec x,r; /* solution, residual vectors */
72: Mat J = NULL; /* Jacobian matrix */
73: AppCtx user; /* user-defined work context */
74: PetscInt its; /* iterations for convergence */
75: MatFDColoring matfdcoloring = NULL;
76: PetscBool matrix_free = PETSC_FALSE,coloring = PETSC_FALSE, coloring_ds = PETSC_FALSE,local_coloring = PETSC_FALSE;
78: PetscReal bratu_lambda_max = 6.81,bratu_lambda_min = 0.,fnorm;
80: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
81: Initialize program
82: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
84: PetscInitialize(&argc,&argv,(char*)0,help);if (ierr) return ierr;
86: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
87: Initialize problem parameters
88: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
89: user.param = 6.0;
90: PetscOptionsGetReal(NULL,NULL,"-par",&user.param,NULL);
91: if (user.param >= bratu_lambda_max || user.param <= bratu_lambda_min) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Lambda is out of range");
93: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
94: Create nonlinear solver context
95: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
96: SNESCreate(PETSC_COMM_WORLD,&snes);
98: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
99: Create distributed array (DMDA) to manage parallel grid and vectors
100: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
101: DMDACreate3d(PETSC_COMM_WORLD,DM_BOUNDARY_NONE,DM_BOUNDARY_NONE,DM_BOUNDARY_NONE,DMDA_STENCIL_STAR,4,4,4,PETSC_DECIDE,PETSC_DECIDE,PETSC_DECIDE,1,1,NULL,NULL,NULL,&user.da);
102: DMSetFromOptions(user.da);
103: DMSetUp(user.da);
104: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
105: Extract global vectors from DMDA; then duplicate for remaining
106: vectors that are the same types
107: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
108: DMCreateGlobalVector(user.da,&x);
109: VecDuplicate(x,&r);
111: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
112: Set function evaluation routine and vector
113: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
114: SNESSetFunction(snes,r,FormFunction,(void*)&user);
116: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
117: Create matrix data structure; set Jacobian evaluation routine
119: Set Jacobian matrix data structure and default Jacobian evaluation
120: routine. User can override with:
121: -snes_mf : matrix-free Newton-Krylov method with no preconditioning
122: (unless user explicitly sets preconditioner)
123: -snes_mf_operator : form preconditioning matrix as set by the user,
124: but use matrix-free approx for Jacobian-vector
125: products within Newton-Krylov method
126: -fdcoloring : using finite differences with coloring to compute the Jacobian
128: Note one can use -matfd_coloring wp or ds the only reason for the -fdcoloring_ds option
129: below is to test the call to MatFDColoringSetType().
130: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
131: PetscOptionsGetBool(NULL,NULL,"-snes_mf",&matrix_free,NULL);
132: PetscOptionsGetBool(NULL,NULL,"-fdcoloring",&coloring,NULL);
133: PetscOptionsGetBool(NULL,NULL,"-fdcoloring_ds",&coloring_ds,NULL);
134: PetscOptionsGetBool(NULL,NULL,"-fdcoloring_local",&local_coloring,NULL);
135: if (!matrix_free) {
136: DMSetMatType(user.da,MATAIJ);
137: DMCreateMatrix(user.da,&J);
138: if (coloring) {
139: ISColoring iscoloring;
140: if (!local_coloring) {
141: DMCreateColoring(user.da,IS_COLORING_GLOBAL,&iscoloring);
142: MatFDColoringCreate(J,iscoloring,&matfdcoloring);
143: MatFDColoringSetFunction(matfdcoloring,(PetscErrorCode (*)(void))FormFunction,&user);
144: } else {
145: DMCreateColoring(user.da,IS_COLORING_LOCAL,&iscoloring);
146: MatFDColoringCreate(J,iscoloring,&matfdcoloring);
147: MatFDColoringUseDM(J,matfdcoloring);
148: MatFDColoringSetFunction(matfdcoloring,(PetscErrorCode (*)(void))FormFunctionLocal,&user);
149: }
150: if (coloring_ds) {
151: MatFDColoringSetType(matfdcoloring,MATMFFD_DS);
152: }
153: MatFDColoringSetFromOptions(matfdcoloring);
154: MatFDColoringSetUp(J,iscoloring,matfdcoloring);
155: SNESSetJacobian(snes,J,J,SNESComputeJacobianDefaultColor,matfdcoloring);
156: ISColoringDestroy(&iscoloring);
157: } else {
158: SNESSetJacobian(snes,J,J,FormJacobian,&user);
159: }
160: }
162: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
163: Customize nonlinear solver; set runtime options
164: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
165: SNESSetDM(snes,user.da);
166: SNESSetFromOptions(snes);
168: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
169: Evaluate initial guess
170: Note: The user should initialize the vector, x, with the initial guess
171: for the nonlinear solver prior to calling SNESSolve(). In particular,
172: to employ an initial guess of zero, the user should explicitly set
173: this vector to zero by calling VecSet().
174: */
175: FormInitialGuess(&user,x);
177: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
178: Solve nonlinear system
179: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
180: SNESSolve(snes,NULL,x);
181: SNESGetIterationNumber(snes,&its);
183: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
184: Explicitly check norm of the residual of the solution
185: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
186: FormFunction(snes,x,r,(void*)&user);
187: VecNorm(r,NORM_2,&fnorm);
188: PetscPrintf(PETSC_COMM_WORLD,"Number of SNES iterations = %D fnorm %g\n",its,(double)fnorm);
190: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
191: Free work space. All PETSc objects should be destroyed when they
192: are no longer needed.
193: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
195: MatDestroy(&J);
196: VecDestroy(&x);
197: VecDestroy(&r);
198: SNESDestroy(&snes);
199: DMDestroy(&user.da);
200: MatFDColoringDestroy(&matfdcoloring);
201: PetscFinalize();
202: return ierr;
203: }
204: /* ------------------------------------------------------------------- */
205: /*
206: FormInitialGuess - Forms initial approximation.
208: Input Parameters:
209: user - user-defined Section 1.5 Writing Application Codes with PETSc context
210: X - vector
212: Output Parameter:
213: X - vector
214: */
215: PetscErrorCode FormInitialGuess(AppCtx *user,Vec X)
216: {
217: PetscInt i,j,k,Mx,My,Mz,xs,ys,zs,xm,ym,zm;
219: PetscReal lambda,temp1,hx,hy,hz,tempk,tempj;
220: PetscScalar ***x;
223: 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);
225: lambda = user->param;
226: hx = 1.0/(PetscReal)(Mx-1);
227: hy = 1.0/(PetscReal)(My-1);
228: hz = 1.0/(PetscReal)(Mz-1);
229: temp1 = lambda/(lambda + 1.0);
231: /*
232: Get a pointer to vector data.
233: - For default PETSc vectors, VecGetArray() returns a pointer to
234: the data array. Otherwise, the routine is implementation dependent.
235: - You MUST call VecRestoreArray() when you no longer need access to
236: the array.
237: */
238: DMDAVecGetArray(user->da,X,&x);
240: /*
241: Get local grid boundaries (for 3-dimensional DMDA):
242: xs, ys, zs - starting grid indices (no ghost points)
243: xm, ym, zm - widths of local grid (no ghost points)
245: */
246: DMDAGetCorners(user->da,&xs,&ys,&zs,&xm,&ym,&zm);
248: /*
249: Compute initial guess over the locally owned part of the grid
250: */
251: for (k=zs; k<zs+zm; k++) {
252: tempk = (PetscReal)(PetscMin(k,Mz-k-1))*hz;
253: for (j=ys; j<ys+ym; j++) {
254: tempj = PetscMin((PetscReal)(PetscMin(j,My-j-1))*hy,tempk);
255: for (i=xs; i<xs+xm; i++) {
256: if (i == 0 || j == 0 || k == 0 || i == Mx-1 || j == My-1 || k == Mz-1) {
257: /* boundary conditions are all zero Dirichlet */
258: x[k][j][i] = 0.0;
259: } else {
260: x[k][j][i] = temp1*PetscSqrtReal(PetscMin((PetscReal)(PetscMin(i,Mx-i-1))*hx,tempj));
261: }
262: }
263: }
264: }
266: /*
267: Restore vector
268: */
269: DMDAVecRestoreArray(user->da,X,&x);
270: return(0);
271: }
272: /* ------------------------------------------------------------------- */
273: /*
274: FormFunctionLocal - Evaluates nonlinear function, F(x) on a ghosted patch
276: Input Parameters:
277: . snes - the SNES context
278: . localX - input vector, this contains the ghosted region needed
279: . ptr - optional user-defined context, as set by SNESSetFunction()
281: Output Parameter:
282: . F - function vector, this does not contain a ghosted region
283: */
284: PetscErrorCode FormFunctionLocal(SNES snes,Vec localX,Vec F,void *ptr)
285: {
286: AppCtx *user = (AppCtx*)ptr;
288: PetscInt i,j,k,Mx,My,Mz,xs,ys,zs,xm,ym,zm;
289: PetscReal two = 2.0,lambda,hx,hy,hz,hxhzdhy,hyhzdhx,hxhydhz,sc;
290: PetscScalar u_north,u_south,u_east,u_west,u_up,u_down,u,u_xx,u_yy,u_zz,***x,***f;
291: DM da;
294: SNESGetDM(snes,&da);
295: DMDAGetInfo(da,PETSC_IGNORE,&Mx,&My,&Mz,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE);
297: lambda = user->param;
298: hx = 1.0/(PetscReal)(Mx-1);
299: hy = 1.0/(PetscReal)(My-1);
300: hz = 1.0/(PetscReal)(Mz-1);
301: sc = hx*hy*hz*lambda;
302: hxhzdhy = hx*hz/hy;
303: hyhzdhx = hy*hz/hx;
304: hxhydhz = hx*hy/hz;
306: /*
307: Get pointers to vector data
308: */
309: DMDAVecGetArrayRead(da,localX,&x);
310: DMDAVecGetArray(da,F,&f);
312: /*
313: Get local grid boundaries
314: */
315: DMDAGetCorners(da,&xs,&ys,&zs,&xm,&ym,&zm);
317: /*
318: Compute function over the locally owned part of the grid
319: */
320: for (k=zs; k<zs+zm; k++) {
321: for (j=ys; j<ys+ym; j++) {
322: for (i=xs; i<xs+xm; i++) {
323: if (i == 0 || j == 0 || k == 0 || i == Mx-1 || j == My-1 || k == Mz-1) {
324: f[k][j][i] = x[k][j][i];
325: } else {
326: u = x[k][j][i];
327: u_east = x[k][j][i+1];
328: u_west = x[k][j][i-1];
329: u_north = x[k][j+1][i];
330: u_south = x[k][j-1][i];
331: u_up = x[k+1][j][i];
332: u_down = x[k-1][j][i];
333: u_xx = (-u_east + two*u - u_west)*hyhzdhx;
334: u_yy = (-u_north + two*u - u_south)*hxhzdhy;
335: u_zz = (-u_up + two*u - u_down)*hxhydhz;
336: f[k][j][i] = u_xx + u_yy + u_zz - sc*PetscExpScalar(u);
337: }
338: }
339: }
340: }
342: /*
343: Restore vectors
344: */
345: DMDAVecRestoreArrayRead(da,localX,&x);
346: DMDAVecRestoreArray(da,F,&f);
347: PetscLogFlops(11.0*ym*xm);
348: return(0);
349: }
350: /* ------------------------------------------------------------------- */
351: /*
352: FormFunction - Evaluates nonlinear function, F(x) on the entire domain
354: Input Parameters:
355: . snes - the SNES context
356: . X - input vector
357: . ptr - optional user-defined context, as set by SNESSetFunction()
359: Output Parameter:
360: . F - function vector
361: */
362: PetscErrorCode FormFunction(SNES snes,Vec X,Vec F,void *ptr)
363: {
365: Vec localX;
366: DM da;
369: SNESGetDM(snes,&da);
370: DMGetLocalVector(da,&localX);
372: /*
373: Scatter ghost points to local vector,using the 2-step process
374: DMGlobalToLocalBegin(),DMGlobalToLocalEnd().
375: By placing code between these two statements, computations can be
376: done while messages are in transition.
377: */
378: DMGlobalToLocalBegin(da,X,INSERT_VALUES,localX);
379: DMGlobalToLocalEnd(da,X,INSERT_VALUES,localX);
381: FormFunctionLocal(snes,localX,F,ptr);
382: DMRestoreLocalVector(da,&localX);
383: return(0);
384: }
385: /* ------------------------------------------------------------------- */
386: /*
387: FormJacobian - Evaluates Jacobian matrix.
389: Input Parameters:
390: . snes - the SNES context
391: . x - input vector
392: . ptr - optional user-defined context, as set by SNESSetJacobian()
394: Output Parameters:
395: . A - Jacobian matrix
396: . B - optionally different preconditioning matrix
398: */
399: PetscErrorCode FormJacobian(SNES snes,Vec X,Mat J,Mat jac,void *ptr)
400: {
401: AppCtx *user = (AppCtx*)ptr; /* user-defined Section 1.5 Writing Application Codes with PETSc context */
402: Vec localX;
404: PetscInt i,j,k,Mx,My,Mz;
405: MatStencil col[7],row;
406: PetscInt xs,ys,zs,xm,ym,zm;
407: PetscScalar lambda,v[7],hx,hy,hz,hxhzdhy,hyhzdhx,hxhydhz,sc,***x;
408: DM da;
411: SNESGetDM(snes,&da);
412: DMGetLocalVector(da,&localX);
413: DMDAGetInfo(da,PETSC_IGNORE,&Mx,&My,&Mz,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE);
415: lambda = user->param;
416: hx = 1.0/(PetscReal)(Mx-1);
417: hy = 1.0/(PetscReal)(My-1);
418: hz = 1.0/(PetscReal)(Mz-1);
419: sc = hx*hy*hz*lambda;
420: hxhzdhy = hx*hz/hy;
421: hyhzdhx = hy*hz/hx;
422: hxhydhz = hx*hy/hz;
424: /*
425: Scatter ghost points to local vector, using the 2-step process
426: DMGlobalToLocalBegin(), DMGlobalToLocalEnd().
427: By placing code between these two statements, computations can be
428: done while messages are in transition.
429: */
430: DMGlobalToLocalBegin(da,X,INSERT_VALUES,localX);
431: DMGlobalToLocalEnd(da,X,INSERT_VALUES,localX);
433: /*
434: Get pointer to vector data
435: */
436: DMDAVecGetArrayRead(da,localX,&x);
438: /*
439: Get local grid boundaries
440: */
441: DMDAGetCorners(da,&xs,&ys,&zs,&xm,&ym,&zm);
443: /*
444: Compute entries for the locally owned part of the Jacobian.
445: - Currently, all PETSc parallel matrix formats are partitioned by
446: contiguous chunks of rows across the processors.
447: - Each processor needs to insert only elements that it owns
448: locally (but any non-local elements will be sent to the
449: appropriate processor during matrix assembly).
450: - Here, we set all entries for a particular row at once.
451: - We can set matrix entries either using either
452: MatSetValuesLocal() or MatSetValues(), as discussed above.
453: */
454: for (k=zs; k<zs+zm; k++) {
455: for (j=ys; j<ys+ym; j++) {
456: for (i=xs; i<xs+xm; i++) {
457: row.k = k; row.j = j; row.i = i;
458: /* boundary points */
459: if (i == 0 || j == 0 || k == 0|| i == Mx-1 || j == My-1 || k == Mz-1) {
460: v[0] = 1.0;
461: MatSetValuesStencil(jac,1,&row,1,&row,v,INSERT_VALUES);
462: } else {
463: /* interior grid points */
464: v[0] = -hxhydhz; col[0].k=k-1;col[0].j=j; col[0].i = i;
465: v[1] = -hxhzdhy; col[1].k=k; col[1].j=j-1;col[1].i = i;
466: v[2] = -hyhzdhx; col[2].k=k; col[2].j=j; col[2].i = i-1;
467: 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;
468: v[4] = -hyhzdhx; col[4].k=k; col[4].j=j; col[4].i = i+1;
469: v[5] = -hxhzdhy; col[5].k=k; col[5].j=j+1;col[5].i = i;
470: v[6] = -hxhydhz; col[6].k=k+1;col[6].j=j; col[6].i = i;
471: MatSetValuesStencil(jac,1,&row,7,col,v,INSERT_VALUES);
472: }
473: }
474: }
475: }
476: DMDAVecRestoreArrayRead(da,localX,&x);
477: DMRestoreLocalVector(da,&localX);
479: /*
480: Assemble matrix, using the 2-step process:
481: MatAssemblyBegin(), MatAssemblyEnd().
482: */
483: MatAssemblyBegin(jac,MAT_FINAL_ASSEMBLY);
484: MatAssemblyEnd(jac,MAT_FINAL_ASSEMBLY);
486: /*
487: Normally since the matrix has already been assembled above; this
488: would do nothing. But in the matrix free mode -snes_mf_operator
489: this tells the "matrix-free" matrix that a new linear system solve
490: is about to be done.
491: */
493: MatAssemblyBegin(J,MAT_FINAL_ASSEMBLY);
494: MatAssemblyEnd(J,MAT_FINAL_ASSEMBLY);
496: /*
497: Tell the matrix we will never add a new nonzero location to the
498: matrix. If we do, it will generate an error.
499: */
500: MatSetOption(jac,MAT_NEW_NONZERO_LOCATION_ERR,PETSC_TRUE);
501: return(0);
502: }
506: /*TEST
508: test:
509: nsize: 4
510: args: -snes_monitor_short -ksp_gmres_cgs_refinement_type refine_always
512: test:
513: suffix: 2
514: nsize: 4
515: args: -snes_monitor_short -ksp_gmres_cgs_refinement_type refine_always
517: test:
518: suffix: 3
519: nsize: 4
520: args: -fdcoloring -snes_monitor_short -ksp_gmres_cgs_refinement_type refine_always
522: test:
523: suffix: 3_ds
524: nsize: 4
525: args: -fdcoloring -fdcoloring_ds -snes_monitor_short -ksp_gmres_cgs_refinement_type refine_always
527: test:
528: suffix: 4
529: nsize: 4
530: args: -fdcoloring_local -fdcoloring -ksp_monitor_short -da_refine 1
531: requires: !single
533: TEST*/