Actual source code: ex19.c
1: /*$Id: ex19.c,v 1.19 2001/04/10 19:37:05 bsmith Exp $*/
3: static char help[] = "Nonlinear driven cavity with multigrid in 2d.n
4: n
5: The 2D driven cavity problem is solved in a velocity-vorticity formulation.n
6: The flow can be driven with the lid or with bouyancy or both:n
7: -lidvelocity <lid>, where <lid> = dimensionless velocity of lidn
8: -grashof <gr>, where <gr> = dimensionless temperature gradientn
9: -prandtl <pr>, where <pr> = dimensionless thermal/momentum diffusity ration
10: Mesh parameters are:n
11: -mx <xg>, where <xg> = number of grid points in the x-directionn
12: -my <yg>, where <yg> = number of grid points in the y-directionn
13: -printg : print grid informationn
14: Graphics of the contours of (U,V,Omega,T) are available on each grid:n
15: -contours : draw contour plots of solutionnn";
17: /*T
18: Concepts: SNES^solving a system of nonlinear equations (parallel multicomponent example);
19: Concepts: DA^using distributed arrays;
20: Concepts: multicomponent
21: Processors: n
22: T*/
24: /* ------------------------------------------------------------------------
26: This code is the same as ex8.c except it uses a multigrid preconditioner
28: We thank David E. Keyes for contributing the driven cavity discretization
29: within this example code.
31: This problem is modeled by the partial differential equation system
32:
33: - Lap(U) - Grad_y(Omega) = 0
34: - Lap(V) + Grad_x(Omega) = 0
35: - Lap(Omega) + Div([U*Omega,V*Omega]) - GR*Grad_x(T) = 0
36: - Lap(T) + PR*Div([U*T,V*T]) = 0
38: in the unit square, which is uniformly discretized in each of x and
39: y in this simple encoding.
41: No-slip, rigid-wall Dirichlet conditions are used for [U,V].
42: Dirichlet conditions are used for Omega, based on the definition of
43: vorticity: Omega = - Grad_y(U) + Grad_x(V), where along each
44: constant coordinate boundary, the tangential derivative is zero.
45: Dirichlet conditions are used for T on the left and right walls,
46: and insulation homogeneous Neumann conditions are used for T on
47: the top and bottom walls.
49: A finite difference approximation with the usual 5-point stencil
50: is used to discretize the boundary value problem to obtain a
51: nonlinear system of equations. Upwinding is used for the divergence
52: (convective) terms and central for the gradient (source) terms.
53:
54: The Jacobian can be either
55: * formed via finite differencing using coloring (the default), or
56: * applied matrix-free via the option -snes_mf
57: (for larger grid problems this variant may not converge
58: without a preconditioner due to ill-conditioning).
60: ------------------------------------------------------------------------- */
62: /*
63: Include "petscda.h" so that we can use distributed arrays (DAs).
64: Include "petscsnes.h" so that we can use SNES solvers. Note that this
65: file automatically includes:
66: petsc.h - base PETSc routines petscvec.h - vectors
67: petscsys.h - system routines petscmat.h - matrices
68: petscis.h - index sets petscksp.h - Krylov subspace methods
69: petscviewer.h - viewers petscpc.h - preconditioners
70: petscsles.h - linear solvers
71: */
72: #include "petscsnes.h"
73: #include "petscda.h"
75: /*
76: User-defined routines and data structures
77: */
78: typedef struct {
79: Scalar u,v,omega,temp;
80: } Field;
82: extern int FormInitialGuess(SNES,Vec,void*);
83: extern int FormFunction(SNES,Vec,Vec,void*);
84: extern int FormFunctionLocal(Field**x,Field**f,DALocalInfo*info,void*);
86: typedef struct {
87: double lidvelocity,prandtl,grashof; /* physical parameters */
88: PetscTruth draw_contours; /* flag - 1 indicates drawing contours */
89: } AppCtx;
91: int main(int argc,char **argv)
92: {
93: DMMG *dmmg; /* multilevel grid structure */
94: AppCtx user; /* user-defined work context */
95: int mx,my,its;
96: int ierr;
97: MPI_Comm comm;
98: SNES snes;
99: DA da;
100: PetscTruth localfunction = PETSC_TRUE;
102: PetscInitialize(&argc,&argv,(char *)0,help);
103: comm = PETSC_COMM_WORLD;
106: PreLoadBegin(PETSC_TRUE,"SetUp");
107: DMMGCreate(comm,2,&user,&dmmg);
110: /*
111: Create distributed array multigrid object (DMMG) to manage parallel grid and vectors
112: for principal unknowns (x) and governing residuals (f)
113: */
114: DACreate2d(comm,DA_NONPERIODIC,DA_STENCIL_STAR,4,4,PETSC_DECIDE,PETSC_DECIDE,4,1,0,0,&da);
115: DMMGSetDM(dmmg,(DM)da);
116: DADestroy(da);
118: DAGetInfo(DMMGGetDA(dmmg),0,&mx,&my,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,
119: PETSC_IGNORE,PETSC_IGNORE);
120: /*
121: Problem parameters (velocity of lid, prandtl, and grashof numbers)
122: */
123: user.lidvelocity = 1.0/(mx*my);
124: user.prandtl = 1.0;
125: user.grashof = 1.0;
126: PetscOptionsGetDouble(PETSC_NULL,"-lidvelocity",&user.lidvelocity,PETSC_NULL);
127: PetscOptionsGetDouble(PETSC_NULL,"-prandtl",&user.prandtl,PETSC_NULL);
128: PetscOptionsGetDouble(PETSC_NULL,"-grashof",&user.grashof,PETSC_NULL);
129: PetscOptionsHasName(PETSC_NULL,"-contours",&user.draw_contours);
131: DASetFieldName(DMMGGetDA(dmmg),0,"x-velocity");
132: DASetFieldName(DMMGGetDA(dmmg),1,"y-velocity");
133: DASetFieldName(DMMGGetDA(dmmg),2,"Omega");
134: DASetFieldName(DMMGGetDA(dmmg),3,"temperature");
136: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
137: Create user context, set problem data, create vector data structures.
138: Also, compute the initial guess.
139: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
141: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
142: Create nonlinear solver context
143: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
145: PetscOptionsGetLogical(PETSC_NULL,"-localfunction",&localfunction,PETSC_IGNORE);
146: if (localfunction) {
147: DMMGSetSNESLocal(dmmg,(int(*)(Scalar**,Scalar**,DALocalInfo*,void*))FormFunctionLocal,0);
148: } else {
149: DMMGSetSNES(dmmg,FormFunction,0);
150: }
152: PetscPrintf(comm,"lid velocity = %g, prandtl # = %g, grashof # = %gn",
153: user.lidvelocity,user.prandtl,user.grashof);
156: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
157: Solve the nonlinear system
158: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
159: DMMGSetInitialGuess(dmmg,FormInitialGuess);
161: PreLoadStage("Solve");
162: DMMGSolve(dmmg);
164: snes = DMMGGetSNES(dmmg);
165: SNESGetIterationNumber(snes,&its);
166: PetscPrintf(comm,"Number of Newton iterations = %dn", its);
168: /*
169: Visualize solution
170: */
172: if (user.draw_contours) {
173: VecView(DMMGGetx(dmmg),PETSC_VIEWER_DRAW_WORLD);
174: }
176: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
177: Free work space. All PETSc objects should be destroyed when they
178: are no longer needed.
179: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
181: DMMGDestroy(dmmg);
182: PreLoadEnd();
184: PetscFinalize();
185: return 0;
186: }
188: /* ------------------------------------------------------------------- */
191: /*
192: FormInitialGuess - Forms initial approximation.
194: Input Parameters:
195: user - user-defined application context
196: X - vector
198: Output Parameter:
199: X - vector
200: */
201: int FormInitialGuess(SNES snes,Vec X,void *ptr)
202: {
203: DMMG dmmg = (DMMG)ptr;
204: AppCtx *user = (AppCtx*)dmmg->user;
205: DA da = (DA)dmmg->dm;
206: int i,j,mx,ierr,xs,ys,xm,ym;
207: double grashof,dx;
208: Field **x;
210: grashof = user->grashof;
212: DAGetInfo(da,0,&mx,0,0,0,0,0,0,0,0,0);
213: dx = 1.0/(mx-1);
215: /*
216: Get local grid boundaries (for 2-dimensional DA):
217: xs, ys - starting grid indices (no ghost points)
218: xm, ym - widths of local grid (no ghost points)
219: */
220: DAGetCorners(da,&xs,&ys,PETSC_NULL,&xm,&ym,PETSC_NULL);
222: /*
223: Get a pointer to vector data.
224: - For default PETSc vectors, VecGetArray() returns a pointer to
225: the data array. Otherwise, the routine is implementation dependent.
226: - You MUST call VecRestoreArray() when you no longer need access to
227: the array.
228: */
229: DAVecGetArray(da,X,(void**)&x);
231: /*
232: Compute initial guess over the locally owned part of the grid
233: Initial condition is motionless fluid and equilibrium temperature
234: */
235: for (j=ys; j<ys+ym; j++) {
236: for (i=xs; i<xs+xm; i++) {
237: x[j][i].u = 0.0;
238: x[j][i].v = 0.0;
239: x[j][i].omega = 0.0;
240: x[j][i].temp = (grashof>0)*i*dx;
241: }
242: }
244: /*
245: Restore vector
246: */
247: DAVecRestoreArray(da,X,(void**)&x);
248: return 0;
249: }
250: /* ------------------------------------------------------------------- */
251: /*
252: FormFunction - Evaluates the nonlinear function, F(x).
254: Input Parameters:
255: . snes - the SNES context
256: . X - input vector
257: . ptr - optional user-defined context, as set by SNESSetFunction()
259: Output Parameter:
260: . F - function vector
262: Notes:
263: We process the boundary nodes before handling the interior
264: nodes, so that no conditional statements are needed within the
265: double loop over the local grid indices.
266: */
267: int FormFunction(SNES snes,Vec X,Vec F,void *ptr)
268: {
269: DMMG dmmg = (DMMG)ptr;
270: AppCtx *user = (AppCtx*)dmmg->user;
271: int ierr,i,j,mx,my,xs,ys,xm,ym;
272: int xints,xinte,yints,yinte;
273: double two = 2.0,one = 1.0,p5 = 0.5,hx,hy,dhx,dhy,hxdhy,hydhx;
274: double grashof,prandtl,lid;
275: Scalar u,uxx,uyy,vx,vy,avx,avy,vxp,vxm,vyp,vym;
276: Field **x,**f;
277: Vec localX;
278: DA da = (DA)dmmg->dm;
280: DAGetLocalVector((DA)dmmg->dm,&localX);
281: DAGetInfo(da,0,&mx,&my,0,0,0,0,0,0,0,0);
283: grashof = user->grashof;
284: prandtl = user->prandtl;
285: lid = user->lidvelocity;
287: /*
288: Define mesh intervals ratios for uniform grid.
289: [Note: FD formulae below are normalized by multiplying through by
290: local volume element to obtain coefficients O(1) in two dimensions.]
291: */
292: dhx = (double)(mx-1); dhy = (double)(my-1);
293: hx = one/dhx; hy = one/dhy;
294: hxdhy = hx*dhy; hydhx = hy*dhx;
296: /*
297: Scatter ghost points to local vector, using the 2-step process
298: DAGlobalToLocalBegin(), DAGlobalToLocalEnd().
299: By placing code between these two statements, computations can be
300: done while messages are in transition.
301: */
302: DAGlobalToLocalBegin(da,X,INSERT_VALUES,localX);
303: DAGlobalToLocalEnd(da,X,INSERT_VALUES,localX);
305: /*
306: Get pointers to vector data
307: */
308: DAVecGetArray((DA)dmmg->dm,localX,(void**)&x);
309: DAVecGetArray((DA)dmmg->dm,F,(void**)&f);
311: /*
312: Get local grid boundaries
313: */
314: DAGetCorners(da,&xs,&ys,PETSC_NULL,&xm,&ym,PETSC_NULL);
316: /*
317: Compute function over the locally owned part of the grid
318: (physical corner points are set twice to avoid more conditionals).
319: */
320: xints = xs; xinte = xs+xm; yints = ys; yinte = ys+ym;
322: /* Test whether we are on the bottom edge of the global array */
323: if (yints == 0) {
324: j = 0;
325: yints = yints + 1;
326: /* bottom edge */
327: for (i=xs; i<xs+xm; i++) {
328: f[j][i].u = x[j][i].u;
329: f[j][i].v = x[j][i].v;
330: f[j][i].omega = x[j][i].omega + (x[j+1][i].u - x[j][i].u)*dhy;
331: f[j][i].temp = x[j][i].temp-x[j+1][i].temp;
332: }
333: }
335: /* Test whether we are on the top edge of the global array */
336: if (yinte == my) {
337: j = my - 1;
338: yinte = yinte - 1;
339: /* top edge */
340: for (i=xs; i<xs+xm; i++) {
341: f[j][i].u = x[j][i].u - lid;
342: f[j][i].v = x[j][i].v;
343: f[j][i].omega = x[j][i].omega + (x[j][i].u - x[j-1][i].u)*dhy;
344: f[j][i].temp = x[j][i].temp-x[j-1][i].temp;
345: }
346: }
348: /* Test whether we are on the left edge of the global array */
349: if (xints == 0) {
350: i = 0;
351: xints = xints + 1;
352: /* left edge */
353: for (j=ys; j<ys+ym; j++) {
354: f[j][i].u = x[j][i].u;
355: f[j][i].v = x[j][i].v;
356: f[j][i].omega = x[j][i].omega - (x[j][i+1].v - x[j][i].v)*dhx;
357: f[j][i].temp = x[j][i].temp;
358: }
359: }
361: /* Test whether we are on the right edge of the global array */
362: if (xinte == mx) {
363: i = mx - 1;
364: xinte = xinte - 1;
365: /* right edge */
366: for (j=ys; j<ys+ym; j++) {
367: f[j][i].u = x[j][i].u;
368: f[j][i].v = x[j][i].v;
369: f[j][i].omega = x[j][i].omega - (x[j][i].v - x[j][i-1].v)*dhx;
370: f[j][i].temp = x[j][i].temp - (double)(grashof>0);
371: }
372: }
374: /* Compute over the interior points */
375: for (j=yints; j<yinte; j++) {
376: for (i=xints; i<xinte; i++) {
378: /*
379: convective coefficients for upwinding
380: */
381: vx = x[j][i].u; avx = PetscAbsScalar(vx);
382: vxp = p5*(vx+avx); vxm = p5*(vx-avx);
383: vy = x[j][i].v; avy = PetscAbsScalar(vy);
384: vyp = p5*(vy+avy); vym = p5*(vy-avy);
386: /* U velocity */
387: u = x[j][i].u;
388: uxx = (two*u - x[j][i-1].u - x[j][i+1].u)*hydhx;
389: uyy = (two*u - x[j-1][i].u - x[j+1][i].u)*hxdhy;
390: f[j][i].u = uxx + uyy - p5*(x[j+1][i].omega-x[j-1][i].omega)*hx;
392: /* V velocity */
393: u = x[j][i].v;
394: uxx = (two*u - x[j][i-1].v - x[j][i+1].v)*hydhx;
395: uyy = (two*u - x[j-1][i].v - x[j+1][i].v)*hxdhy;
396: f[j][i].v = uxx + uyy + p5*(x[j][i+1].omega-x[j][i-1].omega)*hy;
398: /* Omega */
399: u = x[j][i].omega;
400: uxx = (two*u - x[j][i-1].omega - x[j][i+1].omega)*hydhx;
401: uyy = (two*u - x[j-1][i].omega - x[j+1][i].omega)*hxdhy;
402: f[j][i].omega = uxx + uyy +
403: (vxp*(u - x[j][i-1].omega) +
404: vxm*(x[j][i+1].omega - u)) * hy +
405: (vyp*(u - x[j-1][i].omega) +
406: vym*(x[j+1][i].omega - u)) * hx -
407: p5 * grashof * (x[j][i+1].temp - x[j][i-1].temp) * hy;
409: /* Temperature */
410: u = x[j][i].temp;
411: uxx = (two*u - x[j][i-1].temp - x[j][i+1].temp)*hydhx;
412: uyy = (two*u - x[j-1][i].temp - x[j+1][i].temp)*hxdhy;
413: f[j][i].temp = uxx + uyy + prandtl * (
414: (vxp*(u - x[j][i-1].temp) +
415: vxm*(x[j][i+1].temp - u)) * hy +
416: (vyp*(u - x[j-1][i].temp) +
417: vym*(x[j+1][i].temp - u)) * hx);
418: }
419: }
421: /*
422: Restore vectors
423: */
424: DAVecRestoreArray((DA)dmmg->dm,localX,(void**)&x);
425: DAVecRestoreArray((DA)dmmg->dm,F,(void**)&f);
427: DARestoreLocalVector((DA)dmmg->dm,&localX);
429: /*
430: Flop count (multiply-adds are counted as 2 operations)
431: */
432: PetscLogFlops(84*ym*xm);
434: return 0;
435: }
437: int FormFunctionLocal(Field **x,Field **f,DALocalInfo *info,void *ptr)
438: {
439: AppCtx *user = (AppCtx*)ptr;
440: int ierr,i,j;
441: int xints,xinte,yints,yinte;
442: double hx,hy,dhx,dhy,hxdhy,hydhx;
443: double grashof,prandtl,lid;
444: Scalar u,uxx,uyy,vx,vy,avx,avy,vxp,vxm,vyp,vym;
446: grashof = user->grashof;
447: prandtl = user->prandtl;
448: lid = user->lidvelocity;
450: /*
451: Define mesh intervals ratios for uniform grid.
452: [Note: FD formulae below are normalized by multiplying through by
453: local volume element to obtain coefficients O(1) in two dimensions.]
454: */
455: dhx = (double)(info->mx-1); dhy = (double)(info->my-1);
456: hx = 1.0/dhx; hy = 1.0/dhy;
457: hxdhy = hx*dhy; hydhx = hy*dhx;
459: xints = info->xs; xinte = info->xs+info->xm; yints = info->ys; yinte = info->ys+info->ym;
461: /* Test whether we are on the bottom edge of the global array */
462: if (yints == 0) {
463: j = 0;
464: yints = yints + 1;
465: /* bottom edge */
466: for (i=info->xs; i<info->xs+info->xm; i++) {
467: f[j][i].u = x[j][i].u;
468: f[j][i].v = x[j][i].v;
469: f[j][i].omega = x[j][i].omega + (x[j+1][i].u - x[j][i].u)*dhy;
470: f[j][i].temp = x[j][i].temp-x[j+1][i].temp;
471: }
472: }
474: /* Test whether we are on the top edge of the global array */
475: if (yinte == info->my) {
476: j = info->my - 1;
477: yinte = yinte - 1;
478: /* top edge */
479: for (i=info->xs; i<info->xs+info->xm; i++) {
480: f[j][i].u = x[j][i].u - lid;
481: f[j][i].v = x[j][i].v;
482: f[j][i].omega = x[j][i].omega + (x[j][i].u - x[j-1][i].u)*dhy;
483: f[j][i].temp = x[j][i].temp-x[j-1][i].temp;
484: }
485: }
487: /* Test whether we are on the left edge of the global array */
488: if (xints == 0) {
489: i = 0;
490: xints = xints + 1;
491: /* left edge */
492: for (j=info->ys; j<info->ys+info->ym; j++) {
493: f[j][i].u = x[j][i].u;
494: f[j][i].v = x[j][i].v;
495: f[j][i].omega = x[j][i].omega - (x[j][i+1].v - x[j][i].v)*dhx;
496: f[j][i].temp = x[j][i].temp;
497: }
498: }
500: /* Test whether we are on the right edge of the global array */
501: if (xinte == info->mx) {
502: i = info->mx - 1;
503: xinte = xinte - 1;
504: /* right edge */
505: for (j=info->ys; j<info->ys+info->ym; j++) {
506: f[j][i].u = x[j][i].u;
507: f[j][i].v = x[j][i].v;
508: f[j][i].omega = x[j][i].omega - (x[j][i].v - x[j][i-1].v)*dhx;
509: f[j][i].temp = x[j][i].temp - (double)(grashof>0);
510: }
511: }
513: /* Compute over the interior points */
514: for (j=yints; j<yinte; j++) {
515: for (i=xints; i<xinte; i++) {
517: /*
518: convective coefficients for upwinding
519: */
520: vx = x[j][i].u; avx = PetscAbsScalar(vx);
521: vxp = .5*(vx+avx); vxm = .5*(vx-avx);
522: vy = x[j][i].v; avy = PetscAbsScalar(vy);
523: vyp = .5*(vy+avy); vym = .5*(vy-avy);
525: /* U velocity */
526: u = x[j][i].u;
527: uxx = (2.0*u - x[j][i-1].u - x[j][i+1].u)*hydhx;
528: uyy = (2.0*u - x[j-1][i].u - x[j+1][i].u)*hxdhy;
529: f[j][i].u = uxx + uyy - .5*(x[j+1][i].omega-x[j-1][i].omega)*hx;
531: /* V velocity */
532: u = x[j][i].v;
533: uxx = (2.0*u - x[j][i-1].v - x[j][i+1].v)*hydhx;
534: uyy = (2.0*u - x[j-1][i].v - x[j+1][i].v)*hxdhy;
535: f[j][i].v = uxx + uyy + .5*(x[j][i+1].omega-x[j][i-1].omega)*hy;
537: /* Omega */
538: u = x[j][i].omega;
539: uxx = (2.0*u - x[j][i-1].omega - x[j][i+1].omega)*hydhx;
540: uyy = (2.0*u - x[j-1][i].omega - x[j+1][i].omega)*hxdhy;
541: f[j][i].omega = uxx + uyy +
542: (vxp*(u - x[j][i-1].omega) +
543: vxm*(x[j][i+1].omega - u)) * hy +
544: (vyp*(u - x[j-1][i].omega) +
545: vym*(x[j+1][i].omega - u)) * hx -
546: .5 * grashof * (x[j][i+1].temp - x[j][i-1].temp) * hy;
548: /* Temperature */
549: u = x[j][i].temp;
550: uxx = (2.0*u - x[j][i-1].temp - x[j][i+1].temp)*hydhx;
551: uyy = (2.0*u - x[j-1][i].temp - x[j+1][i].temp)*hxdhy;
552: f[j][i].temp = uxx + uyy + prandtl * (
553: (vxp*(u - x[j][i-1].temp) +
554: vxm*(x[j][i+1].temp - u)) * hy +
555: (vyp*(u - x[j-1][i].temp) +
556: vym*(x[j+1][i].temp - u)) * hx);
557: }
558: }
560: /*
561: Flop count (multiply-adds are counted as 2 operations)
562: */
563: PetscLogFlops(84*info->ym*info->xm);
564: return 0;
565: }