xref: /petsc/src/ts/tutorials/ex26.c (revision e08b1d6d0faae6eca507e20c9d3498f81719d047)
1 
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.
12 
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
16 
17         Todd S. Coffey and C. T. Kelley and David E. Keyes, Pseudotransient
18         Continuation and Differential-Algebraic Equations, 2003.
19 
20       That example was removed because it used obsolete interfaces, but the
21       algorithms from the paper can be reproduced using this example.
22 
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.
30 
31       Comment preserved from snes/tutorials/ex27.c, since removed:
32 
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.
41 
42       See also https://lists.mcs.anl.gov/pipermail/petsc-dev/2010-March/002362.html
43 */
44 
45 /* ------------------------------------------------------------------------
46 
47     We thank David E. Keyes for contributing the driven cavity discretization
48     within this example code.
49 
50     This problem is modeled by the partial differential equation system
51 
52         - Lap(U) - Grad_y(Omega) = 0
53         - Lap(V) + Grad_x(Omega) = 0
54         Omega_t - Lap(Omega) + Div([U*Omega,V*Omega]) - GR*Grad_x(T) = 0
55         T_t - Lap(T) + PR*Div([U*T,V*T]) = 0
56 
57     in the unit square, which is uniformly discretized in each of x and
58     y in this simple encoding.
59 
60     No-slip, rigid-wall Dirichlet conditions are used for [U,V].
61     Dirichlet conditions are used for Omega, based on the definition of
62     vorticity: Omega = - Grad_y(U) + Grad_x(V), where along each
63     constant coordinate boundary, the tangential derivative is zero.
64     Dirichlet conditions are used for T on the left and right walls,
65     and insulation homogeneous Neumann conditions are used for T on
66     the top and bottom walls.
67 
68     A finite difference approximation with the usual 5-point stencil
69     is used to discretize the boundary value problem to obtain a
70     nonlinear system of equations.  Upwinding is used for the divergence
71     (convective) terms and central for the gradient (source) terms.
72 
73     The Jacobian can be either
74       * formed via finite differencing using coloring (the default), or
75       * applied matrix-free via the option -snes_mf
76         (for larger grid problems this variant may not converge
77         without a preconditioner due to ill-conditioning).
78 
79   ------------------------------------------------------------------------- */
80 
81 /*
82    Include "petscdmda.h" so that we can use distributed arrays (DMDAs).
83    Include "petscts.h" so that we can use TS solvers.  Note that this
84    file automatically includes:
85      petscsys.h       - base PETSc routines   petscvec.h - vectors
86      petscmat.h - matrices
87      petscis.h     - index sets            petscksp.h - Krylov subspace methods
88      petscviewer.h - viewers               petscpc.h  - preconditioners
89      petscksp.h   - linear solvers         petscsnes.h - nonlinear solvers
90 */
91 #include <petscts.h>
92 #include <petscdm.h>
93 #include <petscdmda.h>
94 
95 /*
96    User-defined routines and data structures
97 */
98 typedef struct {
99   PetscScalar u,v,omega,temp;
100 } Field;
101 
102 PetscErrorCode FormIFunctionLocal(DMDALocalInfo*,PetscReal,Field**,Field**,Field**,void*);
103 
104 typedef struct {
105   PetscReal   lidvelocity,prandtl,grashof;   /* physical parameters */
106   PetscBool   parabolic;                     /* allow a transient term corresponding roughly to artificial compressibility */
107   PetscReal   cfl_initial;                   /* CFL for first time step */
108 } AppCtx;
109 
110 PetscErrorCode FormInitialSolution(TS,Vec,AppCtx*);
111 
112 int main(int argc,char **argv)
113 {
114   AppCtx            user;             /* user-defined work context */
115   PetscInt          mx,my,steps;
116   TS                ts;
117   DM                da;
118   Vec               X;
119   PetscReal         ftime;
120   TSConvergedReason reason;
121 
122   PetscFunctionBeginUser;
123   PetscCall(PetscInitialize(&argc,&argv,(char*)0,help));
124   PetscCall(TSCreate(PETSC_COMM_WORLD,&ts));
125   PetscCall(DMDACreate2d(PETSC_COMM_WORLD,DM_BOUNDARY_NONE,DM_BOUNDARY_NONE,DMDA_STENCIL_STAR,4,4,PETSC_DECIDE,PETSC_DECIDE,4,1,0,0,&da));
126   PetscCall(DMSetFromOptions(da));
127   PetscCall(DMSetUp(da));
128   PetscCall(TSSetDM(ts,(DM)da));
129 
130   PetscCall(DMDAGetInfo(da,0,&mx,&my,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE));
131   /*
132      Problem parameters (velocity of lid, prandtl, and grashof numbers)
133   */
134   user.lidvelocity = 1.0/(mx*my);
135   user.prandtl     = 1.0;
136   user.grashof     = 1.0;
137   user.parabolic   = PETSC_FALSE;
138   user.cfl_initial = 50.;
139 
140   PetscOptionsBegin(PETSC_COMM_WORLD,NULL,"Driven cavity/natural convection options","");
141   PetscCall(PetscOptionsReal("-lidvelocity","Lid velocity, related to Reynolds number","",user.lidvelocity,&user.lidvelocity,NULL));
142   PetscCall(PetscOptionsReal("-prandtl","Ratio of viscous to thermal diffusivity","",user.prandtl,&user.prandtl,NULL));
143   PetscCall(PetscOptionsReal("-grashof","Ratio of bouyant to viscous forces","",user.grashof,&user.grashof,NULL));
144   PetscCall(PetscOptionsBool("-parabolic","Relax incompressibility to make the system parabolic instead of differential-algebraic","",user.parabolic,&user.parabolic,NULL));
145   PetscCall(PetscOptionsReal("-cfl_initial","Advective CFL for the first time step","",user.cfl_initial,&user.cfl_initial,NULL));
146   PetscOptionsEnd();
147 
148   PetscCall(DMDASetFieldName(da,0,"x-velocity"));
149   PetscCall(DMDASetFieldName(da,1,"y-velocity"));
150   PetscCall(DMDASetFieldName(da,2,"Omega"));
151   PetscCall(DMDASetFieldName(da,3,"temperature"));
152 
153   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
154      Create user context, set problem data, create vector data structures.
155      Also, compute the initial guess.
156      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
157 
158   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
159      Create time integration context
160      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
161   PetscCall(DMSetApplicationContext(da,&user));
162   PetscCall(DMDATSSetIFunctionLocal(da,INSERT_VALUES,(DMDATSIFunctionLocal)FormIFunctionLocal,&user));
163   PetscCall(TSSetMaxSteps(ts,10000));
164   PetscCall(TSSetMaxTime(ts,1e12));
165   PetscCall(TSSetExactFinalTime(ts,TS_EXACTFINALTIME_STEPOVER));
166   PetscCall(TSSetTimeStep(ts,user.cfl_initial/(user.lidvelocity*mx)));
167   PetscCall(TSSetFromOptions(ts));
168 
169   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"%" PetscInt_FMT "x%" PetscInt_FMT " grid, lid velocity = %g, prandtl # = %g, grashof # = %g\n",mx,my,(double)user.lidvelocity,(double)user.prandtl,(double)user.grashof));
170 
171   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
172      Solve the nonlinear system
173      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
174 
175   PetscCall(DMCreateGlobalVector(da,&X));
176   PetscCall(FormInitialSolution(ts,X,&user));
177 
178   PetscCall(TSSolve(ts,X));
179   PetscCall(TSGetSolveTime(ts,&ftime));
180   PetscCall(TSGetStepNumber(ts,&steps));
181   PetscCall(TSGetConvergedReason(ts,&reason));
182 
183   PetscCall(PetscPrintf(PETSC_COMM_WORLD,"%s at time %g after %" PetscInt_FMT " steps\n",TSConvergedReasons[reason],(double)ftime,steps));
184 
185   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
186      Free work space.  All PETSc objects should be destroyed when they
187      are no longer needed.
188      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
189   PetscCall(VecDestroy(&X));
190   PetscCall(DMDestroy(&da));
191   PetscCall(TSDestroy(&ts));
192 
193   PetscCall(PetscFinalize());
194   return 0;
195 }
196 
197 /* ------------------------------------------------------------------- */
198 
199 /*
200    FormInitialSolution - Forms initial approximation.
201 
202    Input Parameters:
203    user - user-defined application context
204    X - vector
205 
206    Output Parameter:
207    X - vector
208  */
209 PetscErrorCode FormInitialSolution(TS ts,Vec X,AppCtx *user)
210 {
211   DM             da;
212   PetscInt       i,j,mx,xs,ys,xm,ym;
213   PetscReal      grashof,dx;
214   Field          **x;
215 
216   grashof = user->grashof;
217   PetscCall(TSGetDM(ts,&da));
218   PetscCall(DMDAGetInfo(da,0,&mx,0,0,0,0,0,0,0,0,0,0,0));
219   dx      = 1.0/(mx-1);
220 
221   /*
222      Get local grid boundaries (for 2-dimensional DMDA):
223        xs, ys   - starting grid indices (no ghost points)
224        xm, ym   - widths of local grid (no ghost points)
225   */
226   PetscCall(DMDAGetCorners(da,&xs,&ys,NULL,&xm,&ym,NULL));
227 
228   /*
229      Get a pointer to vector data.
230        - For default PETSc vectors, VecGetArray() returns a pointer to
231          the data array.  Otherwise, the routine is implementation dependent.
232        - You MUST call VecRestoreArray() when you no longer need access to
233          the array.
234   */
235   PetscCall(DMDAVecGetArray(da,X,&x));
236 
237   /*
238      Compute initial guess over the locally owned part of the grid
239      Initial condition is motionless fluid and equilibrium temperature
240   */
241   for (j=ys; j<ys+ym; j++) {
242     for (i=xs; i<xs+xm; i++) {
243       x[j][i].u     = 0.0;
244       x[j][i].v     = 0.0;
245       x[j][i].omega = 0.0;
246       x[j][i].temp  = (grashof>0)*i*dx;
247     }
248   }
249 
250   /*
251      Restore vector
252   */
253   PetscCall(DMDAVecRestoreArray(da,X,&x));
254   return 0;
255 }
256 
257 PetscErrorCode FormIFunctionLocal(DMDALocalInfo *info,PetscReal ptime,Field **x,Field **xdot,Field **f,void *ptr)
258 {
259   AppCtx         *user = (AppCtx*)ptr;
260   PetscInt       xints,xinte,yints,yinte,i,j;
261   PetscReal      hx,hy,dhx,dhy,hxdhy,hydhx;
262   PetscReal      grashof,prandtl,lid;
263   PetscScalar    u,udot,uxx,uyy,vx,vy,avx,avy,vxp,vxm,vyp,vym;
264 
265   PetscFunctionBeginUser;
266   grashof = user->grashof;
267   prandtl = user->prandtl;
268   lid     = user->lidvelocity;
269 
270   /*
271      Define mesh intervals ratios for uniform grid.
272 
273      Note: FD formulae below are normalized by multiplying through by
274      local volume element (i.e. hx*hy) to obtain coefficients O(1) in two dimensions.
275 
276   */
277   dhx   = (PetscReal)(info->mx-1);  dhy = (PetscReal)(info->my-1);
278   hx    = 1.0/dhx;                   hy = 1.0/dhy;
279   hxdhy = hx*dhy;                 hydhx = hy*dhx;
280 
281   xints = info->xs; xinte = info->xs+info->xm; yints = info->ys; yinte = info->ys+info->ym;
282 
283   /* Test whether we are on the bottom edge of the global array */
284   if (yints == 0) {
285     j     = 0;
286     yints = yints + 1;
287     /* bottom edge */
288     for (i=info->xs; i<info->xs+info->xm; i++) {
289       f[j][i].u     = x[j][i].u;
290       f[j][i].v     = x[j][i].v;
291       f[j][i].omega = x[j][i].omega + (x[j+1][i].u - x[j][i].u)*dhy;
292       f[j][i].temp  = x[j][i].temp-x[j+1][i].temp;
293     }
294   }
295 
296   /* Test whether we are on the top edge of the global array */
297   if (yinte == info->my) {
298     j     = info->my - 1;
299     yinte = yinte - 1;
300     /* top edge */
301     for (i=info->xs; i<info->xs+info->xm; i++) {
302       f[j][i].u     = x[j][i].u - lid;
303       f[j][i].v     = x[j][i].v;
304       f[j][i].omega = x[j][i].omega + (x[j][i].u - x[j-1][i].u)*dhy;
305       f[j][i].temp  = x[j][i].temp-x[j-1][i].temp;
306     }
307   }
308 
309   /* Test whether we are on the left edge of the global array */
310   if (xints == 0) {
311     i     = 0;
312     xints = xints + 1;
313     /* left edge */
314     for (j=info->ys; j<info->ys+info->ym; j++) {
315       f[j][i].u     = x[j][i].u;
316       f[j][i].v     = x[j][i].v;
317       f[j][i].omega = x[j][i].omega - (x[j][i+1].v - x[j][i].v)*dhx;
318       f[j][i].temp  = x[j][i].temp;
319     }
320   }
321 
322   /* Test whether we are on the right edge of the global array */
323   if (xinte == info->mx) {
324     i     = info->mx - 1;
325     xinte = xinte - 1;
326     /* right 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].v - x[j][i-1].v)*dhx;
331       f[j][i].temp  = x[j][i].temp - (PetscReal)(grashof>0);
332     }
333   }
334 
335   /* Compute over the interior points */
336   for (j=yints; j<yinte; j++) {
337     for (i=xints; i<xinte; i++) {
338 
339       /*
340         convective coefficients for upwinding
341       */
342       vx  = x[j][i].u; avx = PetscAbsScalar(vx);
343       vxp = .5*(vx+avx); vxm = .5*(vx-avx);
344       vy  = x[j][i].v; avy = PetscAbsScalar(vy);
345       vyp = .5*(vy+avy); vym = .5*(vy-avy);
346 
347       /* U velocity */
348       u         = x[j][i].u;
349       udot      = user->parabolic ? xdot[j][i].u : 0.;
350       uxx       = (2.0*u - x[j][i-1].u - x[j][i+1].u)*hydhx;
351       uyy       = (2.0*u - x[j-1][i].u - x[j+1][i].u)*hxdhy;
352       f[j][i].u = udot + uxx + uyy - .5*(x[j+1][i].omega-x[j-1][i].omega)*hx;
353 
354       /* V velocity */
355       u         = x[j][i].v;
356       udot      = user->parabolic ? xdot[j][i].v : 0.;
357       uxx       = (2.0*u - x[j][i-1].v - x[j][i+1].v)*hydhx;
358       uyy       = (2.0*u - x[j-1][i].v - x[j+1][i].v)*hxdhy;
359       f[j][i].v = udot + uxx + uyy + .5*(x[j][i+1].omega-x[j][i-1].omega)*hy;
360 
361       /* Omega */
362       u             = x[j][i].omega;
363       uxx           = (2.0*u - x[j][i-1].omega - x[j][i+1].omega)*hydhx;
364       uyy           = (2.0*u - x[j-1][i].omega - x[j+1][i].omega)*hxdhy;
365       f[j][i].omega = (xdot[j][i].omega + uxx + uyy
366                        + (vxp*(u - x[j][i-1].omega)
367                           + vxm*(x[j][i+1].omega - u)) * hy
368                        + (vyp*(u - x[j-1][i].omega)
369                           + vym*(x[j+1][i].omega - u)) * hx
370                        - .5 * grashof * (x[j][i+1].temp - x[j][i-1].temp) * hy);
371 
372       /* Temperature */
373       u            = x[j][i].temp;
374       uxx          = (2.0*u - x[j][i-1].temp - x[j][i+1].temp)*hydhx;
375       uyy          = (2.0*u - x[j-1][i].temp - x[j+1][i].temp)*hxdhy;
376       f[j][i].temp =  (xdot[j][i].temp + uxx + uyy
377                        + prandtl * ((vxp*(u - x[j][i-1].temp)
378                                      + vxm*(x[j][i+1].temp - u)) * hy
379                                     + (vyp*(u - x[j-1][i].temp)
380                                        + vym*(x[j+1][i].temp - u)) * hx));
381     }
382   }
383 
384   /*
385      Flop count (multiply-adds are counted as 2 operations)
386   */
387   PetscCall(PetscLogFlops(84.0*info->ym*info->xm));
388   PetscFunctionReturn(0);
389 }
390 
391 /*TEST
392 
393     test:
394       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'
395       requires: !complex !single
396 
397     test:
398       suffix: 2
399       nsize: 4
400       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'
401       requires: !complex !single
402 
403     test:
404       suffix: 3
405       nsize: 4
406       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
407       requires: !complex !single
408 
409     test:
410       suffix: 4
411       nsize: 2
412       args: -da_refine 1 -lidvelocity 100 -grashof 1e3 -ts_max_steps 10 -ts_rtol 1e-3 -ts_atol 1e-3
413       requires: !complex !single
414 
415     test:
416       suffix: asm
417       nsize: 4
418       args: -da_refine 1 -lidvelocity 100 -grashof 1e3 -ts_max_steps 10 -ts_rtol 1e-3 -ts_atol 1e-3
419       requires: !complex !single
420 
421 TEST*/
422