xref: /petsc/src/ts/tutorials/ex6.c (revision df4cd43f92eaa320656440c40edb1046daee8f75)
1 
2 static char help[] = "Solves a simple time-dependent linear PDE (the heat equation).\n\
3 Input parameters include:\n\
4   -m <points>, where <points> = number of grid points\n\
5   -time_dependent_rhs : Treat the problem as having a time-dependent right-hand side\n\
6   -debug              : Activate debugging printouts\n\
7   -nox                : Deactivate x-window graphics\n\n";
8 
9 /* ------------------------------------------------------------------------
10 
11    This program solves the one-dimensional heat equation (also called the
12    diffusion equation),
13        u_t = u_xx,
14    on the domain 0 <= x <= 1, with the boundary conditions
15        u(t,0) = 0, u(t,1) = 0,
16    and the initial condition
17        u(0,x) = sin(6*pi*x) + 3*sin(2*pi*x).
18    This is a linear, second-order, parabolic equation.
19 
20    We discretize the right-hand side using finite differences with
21    uniform grid spacing h:
22        u_xx = (u_{i+1} - 2u_{i} + u_{i-1})/(h^2)
23    We then demonstrate time evolution using the various TS methods by
24    running the program via
25        ex3 -ts_type <timestepping solver>
26 
27    We compare the approximate solution with the exact solution, given by
28        u_exact(x,t) = exp(-36*pi*pi*t) * sin(6*pi*x) +
29                       3*exp(-4*pi*pi*t) * sin(2*pi*x)
30 
31    Notes:
32    This code demonstrates the TS solver interface to two variants of
33    linear problems, u_t = f(u,t), namely
34      - time-dependent f:   f(u,t) is a function of t
35      - time-independent f: f(u,t) is simply f(u)
36 
37     The parallel version of this code is ts/tutorials/ex4.c
38 
39   ------------------------------------------------------------------------- */
40 
41 /*
42    Include "ts.h" so that we can use TS solvers.  Note that this file
43    automatically includes:
44      petscsys.h  - base PETSc routines   vec.h  - vectors
45      sys.h    - system routines       mat.h  - matrices
46      is.h     - index sets            ksp.h  - Krylov subspace methods
47      viewer.h - viewers               pc.h   - preconditioners
48      snes.h - nonlinear solvers
49 */
50 
51 #include <petscts.h>
52 #include <petscdraw.h>
53 
54 /*
55    User-defined application context - contains data needed by the
56    application-provided call-back routines.
57 */
58 typedef struct {
59   Vec         solution;         /* global exact solution vector */
60   PetscInt    m;                /* total number of grid points */
61   PetscReal   h;                /* mesh width h = 1/(m-1) */
62   PetscBool   debug;            /* flag (1 indicates activation of debugging printouts) */
63   PetscViewer viewer1, viewer2; /* viewers for the solution and error */
64   PetscReal   norm_2, norm_max; /* error norms */
65 } AppCtx;
66 
67 /*
68    User-defined routines
69 */
70 extern PetscErrorCode InitialConditions(Vec, AppCtx *);
71 extern PetscErrorCode RHSMatrixHeat(TS, PetscReal, Vec, Mat, Mat, void *);
72 extern PetscErrorCode Monitor(TS, PetscInt, PetscReal, Vec, void *);
73 extern PetscErrorCode ExactSolution(PetscReal, Vec, AppCtx *);
74 extern PetscErrorCode MyBCRoutine(TS, PetscReal, Vec, void *);
75 
76 int main(int argc, char **argv)
77 {
78   AppCtx      appctx;                 /* user-defined application context */
79   TS          ts;                     /* timestepping context */
80   Mat         A;                      /* matrix data structure */
81   Vec         u;                      /* approximate solution vector */
82   PetscReal   time_total_max = 100.0; /* default max total time */
83   PetscInt    time_steps_max = 100;   /* default max timesteps */
84   PetscDraw   draw;                   /* drawing context */
85   PetscInt    steps, m;
86   PetscMPIInt size;
87   PetscReal   dt;
88   PetscReal   ftime;
89   PetscBool   flg;
90   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
91      Initialize program and set problem parameters
92      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
93 
94   PetscFunctionBeginUser;
95   PetscCall(PetscInitialize(&argc, &argv, (char *)0, help));
96   MPI_Comm_size(PETSC_COMM_WORLD, &size);
97   PetscCheck(size == 1, PETSC_COMM_WORLD, PETSC_ERR_WRONG_MPI_SIZE, "This is a uniprocessor example only!");
98 
99   m = 60;
100   PetscCall(PetscOptionsGetInt(NULL, NULL, "-m", &m, NULL));
101   PetscCall(PetscOptionsHasName(NULL, NULL, "-debug", &appctx.debug));
102 
103   appctx.m        = m;
104   appctx.h        = 1.0 / (m - 1.0);
105   appctx.norm_2   = 0.0;
106   appctx.norm_max = 0.0;
107 
108   PetscCall(PetscPrintf(PETSC_COMM_SELF, "Solving a linear TS problem on 1 processor\n"));
109 
110   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
111      Create vector data structures
112      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
113 
114   /*
115      Create vector data structures for approximate and exact solutions
116   */
117   PetscCall(VecCreateSeq(PETSC_COMM_SELF, m, &u));
118   PetscCall(VecDuplicate(u, &appctx.solution));
119 
120   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
121      Set up displays to show graphs of the solution and error
122      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
123 
124   PetscCall(PetscViewerDrawOpen(PETSC_COMM_SELF, 0, "", 80, 380, 400, 160, &appctx.viewer1));
125   PetscCall(PetscViewerDrawGetDraw(appctx.viewer1, 0, &draw));
126   PetscCall(PetscDrawSetDoubleBuffer(draw));
127   PetscCall(PetscViewerDrawOpen(PETSC_COMM_SELF, 0, "", 80, 0, 400, 160, &appctx.viewer2));
128   PetscCall(PetscViewerDrawGetDraw(appctx.viewer2, 0, &draw));
129   PetscCall(PetscDrawSetDoubleBuffer(draw));
130 
131   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
132      Create timestepping solver context
133      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
134 
135   PetscCall(TSCreate(PETSC_COMM_SELF, &ts));
136   PetscCall(TSSetProblemType(ts, TS_LINEAR));
137 
138   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
139      Set optional user-defined monitoring routine
140      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
141 
142   PetscCall(TSMonitorSet(ts, Monitor, &appctx, NULL));
143 
144   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
145 
146      Create matrix data structure; set matrix evaluation routine.
147      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
148 
149   PetscCall(MatCreate(PETSC_COMM_SELF, &A));
150   PetscCall(MatSetSizes(A, PETSC_DECIDE, PETSC_DECIDE, m, m));
151   PetscCall(MatSetFromOptions(A));
152   PetscCall(MatSetUp(A));
153 
154   PetscCall(PetscOptionsHasName(NULL, NULL, "-time_dependent_rhs", &flg));
155   if (flg) {
156     /*
157        For linear problems with a time-dependent f(u,t) in the equation
158        u_t = f(u,t), the user provides the discretized right-hand-side
159        as a time-dependent matrix.
160     */
161     PetscCall(TSSetRHSFunction(ts, NULL, TSComputeRHSFunctionLinear, &appctx));
162     PetscCall(TSSetRHSJacobian(ts, A, A, RHSMatrixHeat, &appctx));
163   } else {
164     /*
165        For linear problems with a time-independent f(u) in the equation
166        u_t = f(u), the user provides the discretized right-hand-side
167        as a matrix only once, and then sets a null matrix evaluation
168        routine.
169     */
170     PetscCall(RHSMatrixHeat(ts, 0.0, u, A, A, &appctx));
171     PetscCall(TSSetRHSFunction(ts, NULL, TSComputeRHSFunctionLinear, &appctx));
172     PetscCall(TSSetRHSJacobian(ts, A, A, TSComputeRHSJacobianConstant, &appctx));
173   }
174 
175   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
176      Set solution vector and initial timestep
177      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
178 
179   dt = appctx.h * appctx.h / 2.0;
180   PetscCall(TSSetTimeStep(ts, dt));
181   PetscCall(TSSetSolution(ts, u));
182 
183   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
184      Customize timestepping solver:
185        - Set the solution method to be the Backward Euler method.
186        - Set timestepping duration info
187      Then set runtime options, which can override these defaults.
188      For example,
189           -ts_max_steps <maxsteps> -ts_max_time <maxtime>
190      to override the defaults set by TSSetMaxSteps()/TSSetMaxTime().
191      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
192 
193   PetscCall(TSSetMaxSteps(ts, time_steps_max));
194   PetscCall(TSSetMaxTime(ts, time_total_max));
195   PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_STEPOVER));
196   PetscCall(TSSetFromOptions(ts));
197 
198   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
199      Solve the problem
200      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
201 
202   /*
203      Evaluate initial conditions
204   */
205   PetscCall(InitialConditions(u, &appctx));
206 
207   /*
208      Run the timestepping solver
209   */
210   PetscCall(TSSolve(ts, u));
211   PetscCall(TSGetSolveTime(ts, &ftime));
212   PetscCall(TSGetStepNumber(ts, &steps));
213 
214   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
215      View timestepping solver info
216      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
217 
218   PetscCall(PetscPrintf(PETSC_COMM_SELF, "avg. error (2 norm) = %g, avg. error (max norm) = %g\n", (double)(appctx.norm_2 / steps), (double)(appctx.norm_max / steps)));
219   PetscCall(TSView(ts, PETSC_VIEWER_STDOUT_SELF));
220 
221   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
222      Free work space.  All PETSc objects should be destroyed when they
223      are no longer needed.
224      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
225 
226   PetscCall(TSDestroy(&ts));
227   PetscCall(MatDestroy(&A));
228   PetscCall(VecDestroy(&u));
229   PetscCall(PetscViewerDestroy(&appctx.viewer1));
230   PetscCall(PetscViewerDestroy(&appctx.viewer2));
231   PetscCall(VecDestroy(&appctx.solution));
232 
233   /*
234      Always call PetscFinalize() before exiting a program.  This routine
235        - finalizes the PETSc libraries as well as MPI
236        - provides summary and diagnostic information if certain runtime
237          options are chosen (e.g., -log_view).
238   */
239   PetscCall(PetscFinalize());
240   return 0;
241 }
242 /* --------------------------------------------------------------------- */
243 /*
244    InitialConditions - Computes the solution at the initial time.
245 
246    Input Parameter:
247    u - uninitialized solution vector (global)
248    appctx - user-defined application context
249 
250    Output Parameter:
251    u - vector with solution at initial time (global)
252 */
253 PetscErrorCode InitialConditions(Vec u, AppCtx *appctx)
254 {
255   PetscScalar *u_localptr;
256   PetscInt     i;
257 
258   PetscFunctionBeginUser;
259   /*
260     Get a pointer to vector data.
261     - For default PETSc vectors, VecGetArray() returns a pointer to
262       the data array.  Otherwise, the routine is implementation dependent.
263     - You MUST call VecRestoreArray() when you no longer need access to
264       the array.
265     - Note that the Fortran interface to VecGetArray() differs from the
266       C version.  See the users manual for details.
267   */
268   PetscCall(VecGetArray(u, &u_localptr));
269 
270   /*
271      We initialize the solution array by simply writing the solution
272      directly into the array locations.  Alternatively, we could use
273      VecSetValues() or VecSetValuesLocal().
274   */
275   for (i = 0; i < appctx->m; i++) u_localptr[i] = PetscSinReal(PETSC_PI * i * 6. * appctx->h) + 3. * PetscSinReal(PETSC_PI * i * 2. * appctx->h);
276 
277   /*
278      Restore vector
279   */
280   PetscCall(VecRestoreArray(u, &u_localptr));
281 
282   /*
283      Print debugging information if desired
284   */
285   if (appctx->debug) PetscCall(VecView(u, PETSC_VIEWER_STDOUT_SELF));
286 
287   PetscFunctionReturn(PETSC_SUCCESS);
288 }
289 /* --------------------------------------------------------------------- */
290 /*
291    ExactSolution - Computes the exact solution at a given time.
292 
293    Input Parameters:
294    t - current time
295    solution - vector in which exact solution will be computed
296    appctx - user-defined application context
297 
298    Output Parameter:
299    solution - vector with the newly computed exact solution
300 */
301 PetscErrorCode ExactSolution(PetscReal t, Vec solution, AppCtx *appctx)
302 {
303   PetscScalar *s_localptr, h = appctx->h, ex1, ex2, sc1, sc2;
304   PetscInt     i;
305 
306   PetscFunctionBeginUser;
307   /*
308      Get a pointer to vector data.
309   */
310   PetscCall(VecGetArray(solution, &s_localptr));
311 
312   /*
313      Simply write the solution directly into the array locations.
314      Alternatively, we culd use VecSetValues() or VecSetValuesLocal().
315   */
316   ex1 = PetscExpReal(-36. * PETSC_PI * PETSC_PI * t);
317   ex2 = PetscExpReal(-4. * PETSC_PI * PETSC_PI * t);
318   sc1 = PETSC_PI * 6. * h;
319   sc2 = PETSC_PI * 2. * h;
320   for (i = 0; i < appctx->m; i++) s_localptr[i] = PetscSinReal(PetscRealPart(sc1) * (PetscReal)i) * ex1 + 3. * PetscSinReal(PetscRealPart(sc2) * (PetscReal)i) * ex2;
321 
322   /*
323      Restore vector
324   */
325   PetscCall(VecRestoreArray(solution, &s_localptr));
326   PetscFunctionReturn(PETSC_SUCCESS);
327 }
328 /* --------------------------------------------------------------------- */
329 /*
330    Monitor - User-provided routine to monitor the solution computed at
331    each timestep.  This example plots the solution and computes the
332    error in two different norms.
333 
334    This example also demonstrates changing the timestep via TSSetTimeStep().
335 
336    Input Parameters:
337    ts     - the timestep context
338    step   - the count of the current step (with 0 meaning the
339              initial condition)
340    crtime  - the current time
341    u      - the solution at this timestep
342    ctx    - the user-provided context for this monitoring routine.
343             In this case we use the application context which contains
344             information about the problem size, workspace and the exact
345             solution.
346 */
347 PetscErrorCode Monitor(TS ts, PetscInt step, PetscReal crtime, Vec u, void *ctx)
348 {
349   AppCtx   *appctx = (AppCtx *)ctx; /* user-defined application context */
350   PetscReal norm_2, norm_max, dt, dttol;
351   PetscBool flg;
352 
353   PetscFunctionBeginUser;
354   /*
355      View a graph of the current iterate
356   */
357   PetscCall(VecView(u, appctx->viewer2));
358 
359   /*
360      Compute the exact solution
361   */
362   PetscCall(ExactSolution(crtime, appctx->solution, appctx));
363 
364   /*
365      Print debugging information if desired
366   */
367   if (appctx->debug) {
368     PetscCall(PetscPrintf(PETSC_COMM_SELF, "Computed solution vector\n"));
369     PetscCall(VecView(u, PETSC_VIEWER_STDOUT_SELF));
370     PetscCall(PetscPrintf(PETSC_COMM_SELF, "Exact solution vector\n"));
371     PetscCall(VecView(appctx->solution, PETSC_VIEWER_STDOUT_SELF));
372   }
373 
374   /*
375      Compute the 2-norm and max-norm of the error
376   */
377   PetscCall(VecAXPY(appctx->solution, -1.0, u));
378   PetscCall(VecNorm(appctx->solution, NORM_2, &norm_2));
379   norm_2 = PetscSqrtReal(appctx->h) * norm_2;
380   PetscCall(VecNorm(appctx->solution, NORM_MAX, &norm_max));
381 
382   PetscCall(TSGetTimeStep(ts, &dt));
383   if (norm_2 > 1.e-2) PetscCall(PetscPrintf(PETSC_COMM_SELF, "Timestep %" PetscInt_FMT ": step size = %g, time = %g, 2-norm error = %g, max norm error = %g\n", step, (double)dt, (double)crtime, (double)norm_2, (double)norm_max));
384   appctx->norm_2 += norm_2;
385   appctx->norm_max += norm_max;
386 
387   dttol = .0001;
388   PetscCall(PetscOptionsGetReal(NULL, NULL, "-dttol", &dttol, &flg));
389   if (dt < dttol) {
390     dt *= .999;
391     PetscCall(TSSetTimeStep(ts, dt));
392   }
393 
394   /*
395      View a graph of the error
396   */
397   PetscCall(VecView(appctx->solution, appctx->viewer1));
398 
399   /*
400      Print debugging information if desired
401   */
402   if (appctx->debug) {
403     PetscCall(PetscPrintf(PETSC_COMM_SELF, "Error vector\n"));
404     PetscCall(VecView(appctx->solution, PETSC_VIEWER_STDOUT_SELF));
405   }
406 
407   PetscFunctionReturn(PETSC_SUCCESS);
408 }
409 /* --------------------------------------------------------------------- */
410 /*
411    RHSMatrixHeat - User-provided routine to compute the right-hand-side
412    matrix for the heat equation.
413 
414    Input Parameters:
415    ts - the TS context
416    t - current time
417    global_in - global input vector
418    dummy - optional user-defined context, as set by TSetRHSJacobian()
419 
420    Output Parameters:
421    AA - Jacobian matrix
422    BB - optionally different preconditioning matrix
423    str - flag indicating matrix structure
424 
425    Notes:
426    Recall that MatSetValues() uses 0-based row and column numbers
427    in Fortran as well as in C.
428 */
429 PetscErrorCode RHSMatrixHeat(TS ts, PetscReal t, Vec X, Mat AA, Mat BB, void *ctx)
430 {
431   Mat         A      = AA;            /* Jacobian matrix */
432   AppCtx     *appctx = (AppCtx *)ctx; /* user-defined application context */
433   PetscInt    mstart = 0;
434   PetscInt    mend   = appctx->m;
435   PetscInt    i, idx[3];
436   PetscScalar v[3], stwo = -2. / (appctx->h * appctx->h), sone = -.5 * stwo;
437 
438   PetscFunctionBeginUser;
439   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
440      Compute entries for the locally owned part of the matrix
441      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
442   /*
443      Set matrix rows corresponding to boundary data
444   */
445 
446   mstart = 0;
447   v[0]   = 1.0;
448   PetscCall(MatSetValues(A, 1, &mstart, 1, &mstart, v, INSERT_VALUES));
449   mstart++;
450 
451   mend--;
452   v[0] = 1.0;
453   PetscCall(MatSetValues(A, 1, &mend, 1, &mend, v, INSERT_VALUES));
454 
455   /*
456      Set matrix rows corresponding to interior data.  We construct the
457      matrix one row at a time.
458   */
459   v[0] = sone;
460   v[1] = stwo;
461   v[2] = sone;
462   for (i = mstart; i < mend; i++) {
463     idx[0] = i - 1;
464     idx[1] = i;
465     idx[2] = i + 1;
466     PetscCall(MatSetValues(A, 1, &i, 3, idx, v, INSERT_VALUES));
467   }
468 
469   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
470      Complete the matrix assembly process and set some options
471      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
472   /*
473      Assemble matrix, using the 2-step process:
474        MatAssemblyBegin(), MatAssemblyEnd()
475      Computations can be done while messages are in transition
476      by placing code between these two statements.
477   */
478   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
479   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
480 
481   /*
482      Set and option to indicate that we will never add a new nonzero location
483      to the matrix. If we do, it will generate an error.
484   */
485   PetscCall(MatSetOption(A, MAT_NEW_NONZERO_LOCATION_ERR, PETSC_TRUE));
486 
487   PetscFunctionReturn(PETSC_SUCCESS);
488 }
489 /* --------------------------------------------------------------------- */
490 /*
491    Input Parameters:
492    ts - the TS context
493    t - current time
494    f - function
495    ctx - optional user-defined context, as set by TSetBCFunction()
496  */
497 PetscErrorCode MyBCRoutine(TS ts, PetscReal t, Vec f, void *ctx)
498 {
499   AppCtx      *appctx = (AppCtx *)ctx; /* user-defined application context */
500   PetscInt     m      = appctx->m;
501   PetscScalar *fa;
502 
503   PetscFunctionBeginUser;
504   PetscCall(VecGetArray(f, &fa));
505   fa[0]     = 0.0;
506   fa[m - 1] = 1.0;
507   PetscCall(VecRestoreArray(f, &fa));
508   PetscCall(PetscPrintf(PETSC_COMM_SELF, "t=%g\n", (double)t));
509 
510   PetscFunctionReturn(PETSC_SUCCESS);
511 }
512 
513 /*TEST
514 
515     test:
516       args: -nox -ts_max_steps 4
517 
518 TEST*/
519