xref: /petsc/src/ts/tutorials/ex26.c (revision df4cd43f92eaa320656440c40edb1046daee8f75)
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 buoyancy 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 buoyant 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   PetscFunctionBeginUser;
217   grashof = user->grashof;
218   PetscCall(TSGetDM(ts, &da));
219   PetscCall(DMDAGetInfo(da, 0, &mx, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
220   dx = 1.0 / (mx - 1);
221 
222   /*
223      Get local grid boundaries (for 2-dimensional DMDA):
224        xs, ys   - starting grid indices (no ghost points)
225        xm, ym   - widths of local grid (no ghost points)
226   */
227   PetscCall(DMDAGetCorners(da, &xs, &ys, NULL, &xm, &ym, NULL));
228 
229   /*
230      Get a pointer to vector data.
231        - For default PETSc vectors, VecGetArray() returns a pointer to
232          the data array.  Otherwise, the routine is implementation dependent.
233        - You MUST call VecRestoreArray() when you no longer need access to
234          the array.
235   */
236   PetscCall(DMDAVecGetArray(da, X, &x));
237 
238   /*
239      Compute initial guess over the locally owned part of the grid
240      Initial condition is motionless fluid and equilibrium temperature
241   */
242   for (j = ys; j < ys + ym; j++) {
243     for (i = xs; i < xs + xm; i++) {
244       x[j][i].u     = 0.0;
245       x[j][i].v     = 0.0;
246       x[j][i].omega = 0.0;
247       x[j][i].temp  = (grashof > 0) * i * dx;
248     }
249   }
250 
251   /*
252      Restore vector
253   */
254   PetscCall(DMDAVecRestoreArray(da, X, &x));
255   PetscFunctionReturn(PETSC_SUCCESS);
256 }
257 
258 PetscErrorCode FormIFunctionLocal(DMDALocalInfo *info, PetscReal ptime, Field **x, Field **xdot, Field **f, void *ptr)
259 {
260   AppCtx     *user = (AppCtx *)ptr;
261   PetscInt    xints, xinte, yints, yinte, i, j;
262   PetscReal   hx, hy, dhx, dhy, hxdhy, hydhx;
263   PetscReal   grashof, prandtl, lid;
264   PetscScalar u, udot, uxx, uyy, vx, vy, avx, avy, vxp, vxm, vyp, vym;
265 
266   PetscFunctionBeginUser;
267   grashof = user->grashof;
268   prandtl = user->prandtl;
269   lid     = user->lidvelocity;
270 
271   /*
272      Define mesh intervals ratios for uniform grid.
273 
274      Note: FD formulae below are normalized by multiplying through by
275      local volume element (i.e. hx*hy) to obtain coefficients O(1) in two dimensions.
276 
277   */
278   dhx   = (PetscReal)(info->mx - 1);
279   dhy   = (PetscReal)(info->my - 1);
280   hx    = 1.0 / dhx;
281   hy    = 1.0 / dhy;
282   hxdhy = hx * dhy;
283   hydhx = hy * dhx;
284 
285   xints = info->xs;
286   xinte = info->xs + info->xm;
287   yints = info->ys;
288   yinte = info->ys + info->ym;
289 
290   /* Test whether we are on the bottom edge of the global array */
291   if (yints == 0) {
292     j     = 0;
293     yints = yints + 1;
294     /* bottom edge */
295     for (i = info->xs; i < info->xs + info->xm; i++) {
296       f[j][i].u     = x[j][i].u;
297       f[j][i].v     = x[j][i].v;
298       f[j][i].omega = x[j][i].omega + (x[j + 1][i].u - x[j][i].u) * dhy;
299       f[j][i].temp  = x[j][i].temp - x[j + 1][i].temp;
300     }
301   }
302 
303   /* Test whether we are on the top edge of the global array */
304   if (yinte == info->my) {
305     j     = info->my - 1;
306     yinte = yinte - 1;
307     /* top edge */
308     for (i = info->xs; i < info->xs + info->xm; i++) {
309       f[j][i].u     = x[j][i].u - lid;
310       f[j][i].v     = x[j][i].v;
311       f[j][i].omega = x[j][i].omega + (x[j][i].u - x[j - 1][i].u) * dhy;
312       f[j][i].temp  = x[j][i].temp - x[j - 1][i].temp;
313     }
314   }
315 
316   /* Test whether we are on the left edge of the global array */
317   if (xints == 0) {
318     i     = 0;
319     xints = xints + 1;
320     /* left edge */
321     for (j = info->ys; j < info->ys + info->ym; j++) {
322       f[j][i].u     = x[j][i].u;
323       f[j][i].v     = x[j][i].v;
324       f[j][i].omega = x[j][i].omega - (x[j][i + 1].v - x[j][i].v) * dhx;
325       f[j][i].temp  = x[j][i].temp;
326     }
327   }
328 
329   /* Test whether we are on the right edge of the global array */
330   if (xinte == info->mx) {
331     i     = info->mx - 1;
332     xinte = xinte - 1;
333     /* right edge */
334     for (j = info->ys; j < info->ys + info->ym; j++) {
335       f[j][i].u     = x[j][i].u;
336       f[j][i].v     = x[j][i].v;
337       f[j][i].omega = x[j][i].omega - (x[j][i].v - x[j][i - 1].v) * dhx;
338       f[j][i].temp  = x[j][i].temp - (PetscReal)(grashof > 0);
339     }
340   }
341 
342   /* Compute over the interior points */
343   for (j = yints; j < yinte; j++) {
344     for (i = xints; i < xinte; i++) {
345       /*
346         convective coefficients for upwinding
347       */
348       vx  = x[j][i].u;
349       avx = PetscAbsScalar(vx);
350       vxp = .5 * (vx + avx);
351       vxm = .5 * (vx - avx);
352       vy  = x[j][i].v;
353       avy = PetscAbsScalar(vy);
354       vyp = .5 * (vy + avy);
355       vym = .5 * (vy - avy);
356 
357       /* U velocity */
358       u         = x[j][i].u;
359       udot      = user->parabolic ? xdot[j][i].u : 0.;
360       uxx       = (2.0 * u - x[j][i - 1].u - x[j][i + 1].u) * hydhx;
361       uyy       = (2.0 * u - x[j - 1][i].u - x[j + 1][i].u) * hxdhy;
362       f[j][i].u = udot + uxx + uyy - .5 * (x[j + 1][i].omega - x[j - 1][i].omega) * hx;
363 
364       /* V velocity */
365       u         = x[j][i].v;
366       udot      = user->parabolic ? xdot[j][i].v : 0.;
367       uxx       = (2.0 * u - x[j][i - 1].v - x[j][i + 1].v) * hydhx;
368       uyy       = (2.0 * u - x[j - 1][i].v - x[j + 1][i].v) * hxdhy;
369       f[j][i].v = udot + uxx + uyy + .5 * (x[j][i + 1].omega - x[j][i - 1].omega) * hy;
370 
371       /* Omega */
372       u   = x[j][i].omega;
373       uxx = (2.0 * u - x[j][i - 1].omega - x[j][i + 1].omega) * hydhx;
374       uyy = (2.0 * u - x[j - 1][i].omega - x[j + 1][i].omega) * hxdhy;
375       f[j][i].omega = (xdot[j][i].omega + uxx + uyy + (vxp * (u - x[j][i - 1].omega) + vxm * (x[j][i + 1].omega - u)) * hy + (vyp * (u - x[j - 1][i].omega) + vym * (x[j + 1][i].omega - u)) * hx - .5 * grashof * (x[j][i + 1].temp - x[j][i - 1].temp) * hy);
376 
377       /* Temperature */
378       u            = x[j][i].temp;
379       uxx          = (2.0 * u - x[j][i - 1].temp - x[j][i + 1].temp) * hydhx;
380       uyy          = (2.0 * u - x[j - 1][i].temp - x[j + 1][i].temp) * hxdhy;
381       f[j][i].temp = (xdot[j][i].temp + uxx + uyy + prandtl * ((vxp * (u - x[j][i - 1].temp) + vxm * (x[j][i + 1].temp - u)) * hy + (vyp * (u - x[j - 1][i].temp) + vym * (x[j + 1][i].temp - u)) * hx));
382     }
383   }
384 
385   /*
386      Flop count (multiply-adds are counted as 2 operations)
387   */
388   PetscCall(PetscLogFlops(84.0 * info->ym * info->xm));
389   PetscFunctionReturn(PETSC_SUCCESS);
390 }
391 
392 /*TEST
393 
394     test:
395       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'
396       requires: !complex !single
397 
398     test:
399       suffix: 2
400       nsize: 4
401       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'
402       requires: !complex !single
403 
404     test:
405       suffix: 3
406       nsize: 4
407       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
408       requires: !complex !single
409 
410     test:
411       suffix: 4
412       nsize: 2
413       args: -da_refine 1 -lidvelocity 100 -grashof 1e3 -ts_max_steps 10 -ts_rtol 1e-3 -ts_atol 1e-3
414       requires: !complex !single
415 
416     test:
417       suffix: asm
418       nsize: 4
419       args: -da_refine 1 -lidvelocity 100 -grashof 1e3 -ts_max_steps 10 -ts_rtol 1e-3 -ts_atol 1e-3
420       requires: !complex !single
421 
422 TEST*/
423