Actual source code: ex26.c
petsc-3.13.6 2020-09-29
2: static char help[] = "Transient nonlinear driven cavity in 2d.\n\
3: \n\
4: The 2D driven cavity problem is solved in a velocity-vorticity formulation.\n\
5: The flow can be driven with the lid or with bouyancy or both:\n\
6: -lidvelocity <lid>, where <lid> = dimensionless velocity of lid\n\
7: -grashof <gr>, where <gr> = dimensionless temperature gradent\n\
8: -prandtl <pr>, where <pr> = dimensionless thermal/momentum diffusity ratio\n\
9: -contours : draw contour plots of solution\n\n";
10: /*
11: See src/snes/tutorials/ex19.c for the steady-state version.
13: There used to be a SNES example (src/snes/tutorials/ex27.c) that
14: implemented this algorithm without using TS and was used for the numerical
15: results in the paper
17: Todd S. Coffey and C. T. Kelley and David E. Keyes, Pseudotransient
18: Continuation and Differential-Algebraic Equations, 2003.
20: That example was removed because it used obsolete interfaces, but the
21: algorithms from the paper can be reproduced using this example.
23: Note: The paper describes the algorithm as being linearly implicit but the
24: numerical results were created using nonlinearly implicit Euler. The
25: algorithm as described (linearly implicit) is more efficient and is the
26: default when using TSPSEUDO. If you want to reproduce the numerical
27: results from the paper, you'll have to change the SNES to converge the
28: nonlinear solve (e.g., -snes_type newtonls). The DAE versus ODE variants
29: are controlled using the -parabolic option.
31: Comment preserved from snes/tutorials/ex27.c, since removed:
33: [H]owever Figure 3.1 was generated with a slightly different algorithm
34: (see targets runex27 and runex27_p) than described in the paper. In
35: particular, the described algorithm is linearly implicit, advancing to
36: the next step after one Newton step, so that the steady state residual
37: is always used, but the figure was generated by converging each step to
38: a relative tolerance of 1.e-3. On the example problem, setting
39: -snes_type ksponly has only minor impact on number of steps, but
40: significantly reduces the required number of linear solves.
42: See also https://lists.mcs.anl.gov/pipermail/petsc-dev/2010-March/002362.html
43: */
45: /*T
46: Concepts: TS^solving a system of nonlinear equations (parallel multicomponent example);
47: Concepts: DMDA^using distributed arrays;
48: Concepts: TS^multicomponent
49: Concepts: TS^differential-algebraic equation
50: Processors: n
51: T*/
52: /* ------------------------------------------------------------------------
54: We thank David E. Keyes for contributing the driven cavity discretization
55: within this example code.
57: This problem is modeled by the partial differential equation system
59: - Lap(U) - Grad_y(Omega) = 0
60: - Lap(V) + Grad_x(Omega) = 0
61: Omega_t - Lap(Omega) + Div([U*Omega,V*Omega]) - GR*Grad_x(T) = 0
62: T_t - Lap(T) + PR*Div([U*T,V*T]) = 0
64: in the unit square, which is uniformly discretized in each of x and
65: y in this simple encoding.
67: No-slip, rigid-wall Dirichlet conditions are used for [U,V].
68: Dirichlet conditions are used for Omega, based on the definition of
69: vorticity: Omega = - Grad_y(U) + Grad_x(V), where along each
70: constant coordinate boundary, the tangential derivative is zero.
71: Dirichlet conditions are used for T on the left and right walls,
72: and insulation homogeneous Neumann conditions are used for T on
73: the top and bottom walls.
75: A finite difference approximation with the usual 5-point stencil
76: is used to discretize the boundary value problem to obtain a
77: nonlinear system of equations. Upwinding is used for the divergence
78: (convective) terms and central for the gradient (source) terms.
80: The Jacobian can be either
81: * formed via finite differencing using coloring (the default), or
82: * applied matrix-free via the option -snes_mf
83: (for larger grid problems this variant may not converge
84: without a preconditioner due to ill-conditioning).
86: ------------------------------------------------------------------------- */
88: /*
89: Include "petscdmda.h" so that we can use distributed arrays (DMDAs).
90: Include "petscts.h" so that we can use TS solvers. Note that this
91: file automatically includes:
92: petscsys.h - base PETSc routines petscvec.h - vectors
93: petscmat.h - matrices
94: petscis.h - index sets petscksp.h - Krylov subspace methods
95: petscviewer.h - viewers petscpc.h - preconditioners
96: petscksp.h - linear solvers petscsnes.h - nonlinear solvers
97: */
98: #include <petscts.h>
99: #include <petscdm.h>
100: #include <petscdmda.h>
102: /*
103: User-defined routines and data structures
104: */
105: typedef struct {
106: PetscScalar u,v,omega,temp;
107: } Field;
109: PetscErrorCode FormIFunctionLocal(DMDALocalInfo*,PetscReal,Field**,Field**,Field**,void*);
111: typedef struct {
112: PetscReal lidvelocity,prandtl,grashof; /* physical parameters */
113: PetscBool parabolic; /* allow a transient term corresponding roughly to artificial compressibility */
114: PetscReal cfl_initial; /* CFL for first time step */
115: } AppCtx;
117: PetscErrorCode FormInitialSolution(TS,Vec,AppCtx*);
119: int main(int argc,char **argv)
120: {
121: AppCtx user; /* user-defined work context */
122: PetscInt mx,my,steps;
123: PetscErrorCode ierr;
124: TS ts;
125: DM da;
126: Vec X;
127: PetscReal ftime;
128: TSConvergedReason reason;
130: PetscInitialize(&argc,&argv,(char*)0,help);if (ierr) return ierr;
131: TSCreate(PETSC_COMM_WORLD,&ts);
132: DMDACreate2d(PETSC_COMM_WORLD,DM_BOUNDARY_NONE,DM_BOUNDARY_NONE,DMDA_STENCIL_STAR,4,4,PETSC_DECIDE,PETSC_DECIDE,4,1,0,0,&da);
133: DMSetFromOptions(da);
134: DMSetUp(da);
135: TSSetDM(ts,(DM)da);
137: DMDAGetInfo(da,0,&mx,&my,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,
138: PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE);
139: /*
140: Problem parameters (velocity of lid, prandtl, and grashof numbers)
141: */
142: user.lidvelocity = 1.0/(mx*my);
143: user.prandtl = 1.0;
144: user.grashof = 1.0;
145: user.parabolic = PETSC_FALSE;
146: user.cfl_initial = 50.;
148: PetscOptionsBegin(PETSC_COMM_WORLD,NULL,"Driven cavity/natural convection options","");
149: PetscOptionsReal("-lidvelocity","Lid velocity, related to Reynolds number","",user.lidvelocity,&user.lidvelocity,NULL);
150: PetscOptionsReal("-prandtl","Ratio of viscous to thermal diffusivity","",user.prandtl,&user.prandtl,NULL);
151: PetscOptionsReal("-grashof","Ratio of bouyant to viscous forces","",user.grashof,&user.grashof,NULL);
152: PetscOptionsBool("-parabolic","Relax incompressibility to make the system parabolic instead of differential-algebraic","",user.parabolic,&user.parabolic,NULL);
153: PetscOptionsReal("-cfl_initial","Advective CFL for the first time step","",user.cfl_initial,&user.cfl_initial,NULL);
154: PetscOptionsEnd();
156: DMDASetFieldName(da,0,"x-velocity");
157: DMDASetFieldName(da,1,"y-velocity");
158: DMDASetFieldName(da,2,"Omega");
159: DMDASetFieldName(da,3,"temperature");
161: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
162: Create user context, set problem data, create vector data structures.
163: Also, compute the initial guess.
164: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
166: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
167: Create time integration context
168: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
169: DMSetApplicationContext(da,&user);
170: DMDATSSetIFunctionLocal(da,INSERT_VALUES,(DMDATSIFunctionLocal)FormIFunctionLocal,&user);
171: TSSetMaxSteps(ts,10000);
172: TSSetMaxTime(ts,1e12);
173: TSSetExactFinalTime(ts,TS_EXACTFINALTIME_STEPOVER);
174: TSSetTimeStep(ts,user.cfl_initial/(user.lidvelocity*mx));
175: TSSetFromOptions(ts);
177: PetscPrintf(PETSC_COMM_WORLD,"%Dx%D grid, lid velocity = %g, prandtl # = %g, grashof # = %g\n",mx,my,(double)user.lidvelocity,(double)user.prandtl,(double)user.grashof);
180: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
181: Solve the nonlinear system
182: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
184: DMCreateGlobalVector(da,&X);
185: FormInitialSolution(ts,X,&user);
187: TSSolve(ts,X);
188: TSGetSolveTime(ts,&ftime);
189: TSGetStepNumber(ts,&steps);
190: TSGetConvergedReason(ts,&reason);
192: PetscPrintf(PETSC_COMM_WORLD,"%s at time %g after %D steps\n",TSConvergedReasons[reason],(double)ftime,steps);
194: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
195: Free work space. All PETSc objects should be destroyed when they
196: are no longer needed.
197: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
198: VecDestroy(&X);
199: DMDestroy(&da);
200: TSDestroy(&ts);
202: PetscFinalize();
203: return ierr;
204: }
206: /* ------------------------------------------------------------------- */
209: /*
210: FormInitialSolution - Forms initial approximation.
212: Input Parameters:
213: user - user-defined Section 1.5 Writing Application Codes with PETSc context
214: X - vector
216: Output Parameter:
217: X - vector
218: */
219: PetscErrorCode FormInitialSolution(TS ts,Vec X,AppCtx *user)
220: {
221: DM da;
222: PetscInt i,j,mx,xs,ys,xm,ym;
224: PetscReal grashof,dx;
225: Field **x;
227: grashof = user->grashof;
228: TSGetDM(ts,&da);
229: DMDAGetInfo(da,0,&mx,0,0,0,0,0,0,0,0,0,0,0);
230: dx = 1.0/(mx-1);
232: /*
233: Get local grid boundaries (for 2-dimensional DMDA):
234: xs, ys - starting grid indices (no ghost points)
235: xm, ym - widths of local grid (no ghost points)
236: */
237: DMDAGetCorners(da,&xs,&ys,NULL,&xm,&ym,NULL);
239: /*
240: Get a pointer to vector data.
241: - For default PETSc vectors, VecGetArray() returns a pointer to
242: the data array. Otherwise, the routine is implementation dependent.
243: - You MUST call VecRestoreArray() when you no longer need access to
244: the array.
245: */
246: DMDAVecGetArray(da,X,&x);
248: /*
249: Compute initial guess over the locally owned part of the grid
250: Initial condition is motionless fluid and equilibrium temperature
251: */
252: for (j=ys; j<ys+ym; j++) {
253: for (i=xs; i<xs+xm; i++) {
254: x[j][i].u = 0.0;
255: x[j][i].v = 0.0;
256: x[j][i].omega = 0.0;
257: x[j][i].temp = (grashof>0)*i*dx;
258: }
259: }
261: /*
262: Restore vector
263: */
264: DMDAVecRestoreArray(da,X,&x);
265: return 0;
266: }
268: PetscErrorCode FormIFunctionLocal(DMDALocalInfo *info,PetscReal ptime,Field **x,Field **xdot,Field **f,void *ptr)
269: {
270: AppCtx *user = (AppCtx*)ptr;
272: PetscInt xints,xinte,yints,yinte,i,j;
273: PetscReal hx,hy,dhx,dhy,hxdhy,hydhx;
274: PetscReal grashof,prandtl,lid;
275: PetscScalar u,udot,uxx,uyy,vx,vy,avx,avy,vxp,vxm,vyp,vym;
278: grashof = user->grashof;
279: prandtl = user->prandtl;
280: lid = user->lidvelocity;
282: /*
283: Define mesh intervals ratios for uniform grid.
285: Note: FD formulae below are normalized by multiplying through by
286: local volume element (i.e. hx*hy) to obtain coefficients O(1) in two dimensions.
289: */
290: dhx = (PetscReal)(info->mx-1); dhy = (PetscReal)(info->my-1);
291: hx = 1.0/dhx; hy = 1.0/dhy;
292: hxdhy = hx*dhy; hydhx = hy*dhx;
294: xints = info->xs; xinte = info->xs+info->xm; yints = info->ys; yinte = info->ys+info->ym;
296: /* Test whether we are on the bottom edge of the global array */
297: if (yints == 0) {
298: j = 0;
299: yints = yints + 1;
300: /* bottom edge */
301: for (i=info->xs; i<info->xs+info->xm; i++) {
302: f[j][i].u = x[j][i].u;
303: f[j][i].v = x[j][i].v;
304: f[j][i].omega = x[j][i].omega + (x[j+1][i].u - x[j][i].u)*dhy;
305: f[j][i].temp = x[j][i].temp-x[j+1][i].temp;
306: }
307: }
309: /* Test whether we are on the top edge of the global array */
310: if (yinte == info->my) {
311: j = info->my - 1;
312: yinte = yinte - 1;
313: /* top edge */
314: for (i=info->xs; i<info->xs+info->xm; i++) {
315: f[j][i].u = x[j][i].u - lid;
316: f[j][i].v = x[j][i].v;
317: f[j][i].omega = x[j][i].omega + (x[j][i].u - x[j-1][i].u)*dhy;
318: f[j][i].temp = x[j][i].temp-x[j-1][i].temp;
319: }
320: }
322: /* Test whether we are on the left edge of the global array */
323: if (xints == 0) {
324: i = 0;
325: xints = xints + 1;
326: /* left edge */
327: for (j=info->ys; j<info->ys+info->ym; j++) {
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][i+1].v - x[j][i].v)*dhx;
331: f[j][i].temp = x[j][i].temp;
332: }
333: }
335: /* Test whether we are on the right edge of the global array */
336: if (xinte == info->mx) {
337: i = info->mx - 1;
338: xinte = xinte - 1;
339: /* right edge */
340: for (j=info->ys; j<info->ys+info->ym; j++) {
341: f[j][i].u = x[j][i].u;
342: f[j][i].v = x[j][i].v;
343: f[j][i].omega = x[j][i].omega - (x[j][i].v - x[j][i-1].v)*dhx;
344: f[j][i].temp = x[j][i].temp - (PetscReal)(grashof>0);
345: }
346: }
348: /* Compute over the interior points */
349: for (j=yints; j<yinte; j++) {
350: for (i=xints; i<xinte; i++) {
352: /*
353: convective coefficients for upwinding
354: */
355: vx = x[j][i].u; avx = PetscAbsScalar(vx);
356: vxp = .5*(vx+avx); vxm = .5*(vx-avx);
357: vy = x[j][i].v; avy = PetscAbsScalar(vy);
358: vyp = .5*(vy+avy); vym = .5*(vy-avy);
360: /* U velocity */
361: u = x[j][i].u;
362: udot = user->parabolic ? xdot[j][i].u : 0.;
363: uxx = (2.0*u - x[j][i-1].u - x[j][i+1].u)*hydhx;
364: uyy = (2.0*u - x[j-1][i].u - x[j+1][i].u)*hxdhy;
365: f[j][i].u = udot + uxx + uyy - .5*(x[j+1][i].omega-x[j-1][i].omega)*hx;
367: /* V velocity */
368: u = x[j][i].v;
369: udot = user->parabolic ? xdot[j][i].v : 0.;
370: uxx = (2.0*u - x[j][i-1].v - x[j][i+1].v)*hydhx;
371: uyy = (2.0*u - x[j-1][i].v - x[j+1][i].v)*hxdhy;
372: f[j][i].v = udot + uxx + uyy + .5*(x[j][i+1].omega-x[j][i-1].omega)*hy;
374: /* Omega */
375: u = x[j][i].omega;
376: uxx = (2.0*u - x[j][i-1].omega - x[j][i+1].omega)*hydhx;
377: uyy = (2.0*u - x[j-1][i].omega - x[j+1][i].omega)*hxdhy;
378: f[j][i].omega = (xdot[j][i].omega + uxx + uyy
379: + (vxp*(u - x[j][i-1].omega)
380: + vxm*(x[j][i+1].omega - u)) * hy
381: + (vyp*(u - x[j-1][i].omega)
382: + vym*(x[j+1][i].omega - u)) * hx
383: - .5 * grashof * (x[j][i+1].temp - x[j][i-1].temp) * hy);
385: /* Temperature */
386: u = x[j][i].temp;
387: uxx = (2.0*u - x[j][i-1].temp - x[j][i+1].temp)*hydhx;
388: uyy = (2.0*u - x[j-1][i].temp - x[j+1][i].temp)*hxdhy;
389: f[j][i].temp = (xdot[j][i].temp + uxx + uyy
390: + prandtl * ((vxp*(u - x[j][i-1].temp)
391: + vxm*(x[j][i+1].temp - u)) * hy
392: + (vyp*(u - x[j-1][i].temp)
393: + vym*(x[j+1][i].temp - u)) * hx));
394: }
395: }
397: /*
398: Flop count (multiply-adds are counted as 2 operations)
399: */
400: PetscLogFlops(84.0*info->ym*info->xm);
401: return(0);
402: }
404: /*TEST
406: test:
407: args: -da_grid_x 20 -da_grid_y 20 -lidvelocity 100 -grashof 1e3 -ts_max_steps 100 -ts_rtol 1e-3 -ts_atol 1e-3 -ts_type rosw -ts_rosw_type ra3pw -ts_monitor -ts_monitor_solution_vtk 'foo-%03D.vts'
408: requires: !complex !single
410: test:
411: suffix: 2
412: nsize: 4
413: args: -da_grid_x 20 -da_grid_y 20 -lidvelocity 100 -grashof 1e3 -ts_max_steps 100 -ts_rtol 1e-3 -ts_atol 1e-3 -ts_type rosw -ts_rosw_type ra3pw -ts_monitor -ts_monitor_solution_vtk 'foo-%03D.vts'
414: requires: !complex !single
416: test:
417: suffix: 3
418: nsize: 4
419: args: -da_refine 2 -lidvelocity 100 -grashof 1e3 -ts_max_steps 10 -ts_rtol 1e-3 -ts_atol 1e-3 -pc_type none -ts_type beuler -ts_monitor -snes_monitor_short -snes_type aspin -da_overlap 4
420: requires: !complex !single
422: test:
423: suffix: 4
424: nsize: 2
425: args: -da_refine 1 -lidvelocity 100 -grashof 1e3 -ts_max_steps 10 -ts_rtol 1e-3 -ts_atol 1e-3
426: requires: !complex !single
428: test:
429: suffix: asm
430: nsize: 4
431: args: -da_refine 1 -lidvelocity 100 -grashof 1e3 -ts_max_steps 10 -ts_rtol 1e-3 -ts_atol 1e-3
432: requires: !complex !single
434: TEST*/