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