xref: /petsc/src/ts/tutorials/ex8.c (revision 58d68138c660dfb4e9f5b03334792cd4f2ffd7cc)
1 
2 static char help[] = "Nonlinear DAE benchmark problems.\n";
3 
4 /*
5    Include "petscts.h" so that we can use TS solvers.  Note that this
6    file automatically includes:
7      petscsys.h       - base PETSc routines   petscvec.h - vectors
8      petscmat.h - matrices
9      petscis.h     - index sets            petscksp.h - Krylov subspace methods
10      petscviewer.h - viewers               petscpc.h  - preconditioners
11      petscksp.h   - linear solvers
12 */
13 #include <petscts.h>
14 
15 typedef struct _Problem *Problem;
16 struct _Problem {
17   PetscErrorCode (*destroy)(Problem);
18   TSIFunction function;
19   TSIJacobian jacobian;
20   PetscErrorCode (*solution)(PetscReal, Vec, void *);
21   MPI_Comm  comm;
22   PetscReal final_time;
23   PetscInt  n;
24   PetscBool hasexact;
25   void     *data;
26 };
27 
28 /*
29       Stiff 3-variable system from chemical reactions, due to Robertson (1966), problem ROBER in Hairer&Wanner, ODE 2, 1996
30 */
31 static PetscErrorCode RoberFunction(TS ts, PetscReal t, Vec X, Vec Xdot, Vec F, void *ctx) {
32   PetscScalar       *f;
33   const PetscScalar *x, *xdot;
34 
35   PetscFunctionBeginUser;
36   PetscCall(VecGetArrayRead(X, &x));
37   PetscCall(VecGetArrayRead(Xdot, &xdot));
38   PetscCall(VecGetArray(F, &f));
39   f[0] = xdot[0] + 0.04 * x[0] - 1e4 * x[1] * x[2];
40   f[1] = xdot[1] - 0.04 * x[0] + 1e4 * x[1] * x[2] + 3e7 * PetscSqr(x[1]);
41   f[2] = xdot[2] - 3e7 * PetscSqr(x[1]);
42   PetscCall(VecRestoreArrayRead(X, &x));
43   PetscCall(VecRestoreArrayRead(Xdot, &xdot));
44   PetscCall(VecRestoreArray(F, &f));
45   PetscFunctionReturn(0);
46 }
47 
48 static PetscErrorCode RoberJacobian(TS ts, PetscReal t, Vec X, Vec Xdot, PetscReal a, Mat A, Mat B, void *ctx) {
49   PetscInt           rowcol[] = {0, 1, 2};
50   PetscScalar        J[3][3];
51   const PetscScalar *x, *xdot;
52 
53   PetscFunctionBeginUser;
54   PetscCall(VecGetArrayRead(X, &x));
55   PetscCall(VecGetArrayRead(Xdot, &xdot));
56   J[0][0] = a + 0.04;
57   J[0][1] = -1e4 * x[2];
58   J[0][2] = -1e4 * x[1];
59   J[1][0] = -0.04;
60   J[1][1] = a + 1e4 * x[2] + 3e7 * 2 * x[1];
61   J[1][2] = 1e4 * x[1];
62   J[2][0] = 0;
63   J[2][1] = -3e7 * 2 * x[1];
64   J[2][2] = a;
65   PetscCall(MatSetValues(B, 3, rowcol, 3, rowcol, &J[0][0], INSERT_VALUES));
66   PetscCall(VecRestoreArrayRead(X, &x));
67   PetscCall(VecRestoreArrayRead(Xdot, &xdot));
68 
69   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
70   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
71   if (A != B) {
72     PetscCall(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY));
73     PetscCall(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY));
74   }
75   PetscFunctionReturn(0);
76 }
77 
78 static PetscErrorCode RoberSolution(PetscReal t, Vec X, void *ctx) {
79   PetscScalar *x;
80 
81   PetscFunctionBeginUser;
82   PetscCheck(t == 0, PETSC_COMM_WORLD, PETSC_ERR_SUP, "not implemented");
83   PetscCall(VecGetArray(X, &x));
84   x[0] = 1;
85   x[1] = 0;
86   x[2] = 0;
87   PetscCall(VecRestoreArray(X, &x));
88   PetscFunctionReturn(0);
89 }
90 
91 static PetscErrorCode RoberCreate(Problem p) {
92   PetscFunctionBeginUser;
93   p->destroy    = 0;
94   p->function   = &RoberFunction;
95   p->jacobian   = &RoberJacobian;
96   p->solution   = &RoberSolution;
97   p->final_time = 1e11;
98   p->n          = 3;
99   PetscFunctionReturn(0);
100 }
101 
102 /*
103      Stiff scalar valued problem
104 */
105 
106 typedef struct {
107   PetscReal lambda;
108 } CECtx;
109 
110 static PetscErrorCode CEDestroy(Problem p) {
111   PetscFunctionBeginUser;
112   PetscCall(PetscFree(p->data));
113   PetscFunctionReturn(0);
114 }
115 
116 static PetscErrorCode CEFunction(TS ts, PetscReal t, Vec X, Vec Xdot, Vec F, void *ctx) {
117   PetscReal          l = ((CECtx *)ctx)->lambda;
118   PetscScalar       *f;
119   const PetscScalar *x, *xdot;
120 
121   PetscFunctionBeginUser;
122   PetscCall(VecGetArrayRead(X, &x));
123   PetscCall(VecGetArrayRead(Xdot, &xdot));
124   PetscCall(VecGetArray(F, &f));
125   f[0] = xdot[0] + l * (x[0] - PetscCosReal(t));
126 #if 0
127   PetscCall(PetscPrintf(PETSC_COMM_WORLD," f(t=%g,x=%g,xdot=%g) = %g\n",(double)t,(double)x[0],(double)xdot[0],(double)f[0]));
128 #endif
129   PetscCall(VecRestoreArrayRead(X, &x));
130   PetscCall(VecRestoreArrayRead(Xdot, &xdot));
131   PetscCall(VecRestoreArray(F, &f));
132   PetscFunctionReturn(0);
133 }
134 
135 static PetscErrorCode CEJacobian(TS ts, PetscReal t, Vec X, Vec Xdot, PetscReal a, Mat A, Mat B, void *ctx) {
136   PetscReal          l        = ((CECtx *)ctx)->lambda;
137   PetscInt           rowcol[] = {0};
138   PetscScalar        J[1][1];
139   const PetscScalar *x, *xdot;
140 
141   PetscFunctionBeginUser;
142   PetscCall(VecGetArrayRead(X, &x));
143   PetscCall(VecGetArrayRead(Xdot, &xdot));
144   J[0][0] = a + l;
145   PetscCall(MatSetValues(B, 1, rowcol, 1, rowcol, &J[0][0], INSERT_VALUES));
146   PetscCall(VecRestoreArrayRead(X, &x));
147   PetscCall(VecRestoreArrayRead(Xdot, &xdot));
148 
149   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
150   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
151   if (A != B) {
152     PetscCall(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY));
153     PetscCall(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY));
154   }
155   PetscFunctionReturn(0);
156 }
157 
158 static PetscErrorCode CESolution(PetscReal t, Vec X, void *ctx) {
159   PetscReal    l = ((CECtx *)ctx)->lambda;
160   PetscScalar *x;
161 
162   PetscFunctionBeginUser;
163   PetscCall(VecGetArray(X, &x));
164   x[0] = l / (l * l + 1) * (l * PetscCosReal(t) + PetscSinReal(t)) - l * l / (l * l + 1) * PetscExpReal(-l * t);
165   PetscCall(VecRestoreArray(X, &x));
166   PetscFunctionReturn(0);
167 }
168 
169 static PetscErrorCode CECreate(Problem p) {
170   CECtx *ce;
171 
172   PetscFunctionBeginUser;
173   PetscCall(PetscMalloc(sizeof(CECtx), &ce));
174   p->data = (void *)ce;
175 
176   p->destroy    = &CEDestroy;
177   p->function   = &CEFunction;
178   p->jacobian   = &CEJacobian;
179   p->solution   = &CESolution;
180   p->final_time = 10;
181   p->n          = 1;
182   p->hasexact   = PETSC_TRUE;
183 
184   ce->lambda = 10;
185   PetscOptionsBegin(p->comm, NULL, "CE options", "");
186   { PetscCall(PetscOptionsReal("-problem_ce_lambda", "Parameter controlling stiffness: xdot + lambda*(x - cos(t))", "", ce->lambda, &ce->lambda, NULL)); }
187   PetscOptionsEnd();
188   PetscFunctionReturn(0);
189 }
190 
191 /*
192    Stiff 3-variable oscillatory system from chemical reactions. problem OREGO in Hairer&Wanner
193 */
194 static PetscErrorCode OregoFunction(TS ts, PetscReal t, Vec X, Vec Xdot, Vec F, void *ctx) {
195   PetscScalar       *f;
196   const PetscScalar *x, *xdot;
197 
198   PetscFunctionBeginUser;
199   PetscCall(VecGetArrayRead(X, &x));
200   PetscCall(VecGetArrayRead(Xdot, &xdot));
201   PetscCall(VecGetArray(F, &f));
202   f[0] = xdot[0] - 77.27 * (x[1] + x[0] * (1. - 8.375e-6 * x[0] - x[1]));
203   f[1] = xdot[1] - 1 / 77.27 * (x[2] - (1. + x[0]) * x[1]);
204   f[2] = xdot[2] - 0.161 * (x[0] - x[2]);
205   PetscCall(VecRestoreArrayRead(X, &x));
206   PetscCall(VecRestoreArrayRead(Xdot, &xdot));
207   PetscCall(VecRestoreArray(F, &f));
208   PetscFunctionReturn(0);
209 }
210 
211 static PetscErrorCode OregoJacobian(TS ts, PetscReal t, Vec X, Vec Xdot, PetscReal a, Mat A, Mat B, void *ctx) {
212   PetscInt           rowcol[] = {0, 1, 2};
213   PetscScalar        J[3][3];
214   const PetscScalar *x, *xdot;
215 
216   PetscFunctionBeginUser;
217   PetscCall(VecGetArrayRead(X, &x));
218   PetscCall(VecGetArrayRead(Xdot, &xdot));
219   J[0][0] = a - 77.27 * ((1. - 8.375e-6 * x[0] - x[1]) - 8.375e-6 * x[0]);
220   J[0][1] = -77.27 * (1. - x[0]);
221   J[0][2] = 0;
222   J[1][0] = 1. / 77.27 * x[1];
223   J[1][1] = a + 1. / 77.27 * (1. + x[0]);
224   J[1][2] = -1. / 77.27;
225   J[2][0] = -0.161;
226   J[2][1] = 0;
227   J[2][2] = a + 0.161;
228   PetscCall(MatSetValues(B, 3, rowcol, 3, rowcol, &J[0][0], INSERT_VALUES));
229   PetscCall(VecRestoreArrayRead(X, &x));
230   PetscCall(VecRestoreArrayRead(Xdot, &xdot));
231 
232   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
233   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
234   if (A != B) {
235     PetscCall(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY));
236     PetscCall(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY));
237   }
238   PetscFunctionReturn(0);
239 }
240 
241 static PetscErrorCode OregoSolution(PetscReal t, Vec X, void *ctx) {
242   PetscScalar *x;
243 
244   PetscFunctionBeginUser;
245   PetscCheck(t == 0, PETSC_COMM_WORLD, PETSC_ERR_SUP, "not implemented");
246   PetscCall(VecGetArray(X, &x));
247   x[0] = 1;
248   x[1] = 2;
249   x[2] = 3;
250   PetscCall(VecRestoreArray(X, &x));
251   PetscFunctionReturn(0);
252 }
253 
254 static PetscErrorCode OregoCreate(Problem p) {
255   PetscFunctionBeginUser;
256   p->destroy    = 0;
257   p->function   = &OregoFunction;
258   p->jacobian   = &OregoJacobian;
259   p->solution   = &OregoSolution;
260   p->final_time = 360;
261   p->n          = 3;
262   PetscFunctionReturn(0);
263 }
264 
265 /*
266    User-defined monitor for comparing to exact solutions when possible
267 */
268 typedef struct {
269   MPI_Comm comm;
270   Problem  problem;
271   Vec      x;
272 } MonitorCtx;
273 
274 static PetscErrorCode MonitorError(TS ts, PetscInt step, PetscReal t, Vec x, void *ctx) {
275   MonitorCtx *mon = (MonitorCtx *)ctx;
276   PetscReal   h, nrm_x, nrm_exact, nrm_diff;
277 
278   PetscFunctionBeginUser;
279   if (!mon->problem->solution) PetscFunctionReturn(0);
280   PetscCall((*mon->problem->solution)(t, mon->x, mon->problem->data));
281   PetscCall(VecNorm(x, NORM_2, &nrm_x));
282   PetscCall(VecNorm(mon->x, NORM_2, &nrm_exact));
283   PetscCall(VecAYPX(mon->x, -1, x));
284   PetscCall(VecNorm(mon->x, NORM_2, &nrm_diff));
285   PetscCall(TSGetTimeStep(ts, &h));
286   if (step < 0) { PetscCall(PetscPrintf(mon->comm, "Interpolated final solution ")); }
287   PetscCall(PetscPrintf(mon->comm, "step %4" PetscInt_FMT " t=%12.8e h=% 8.2e  |x|=%9.2e  |x_e|=%9.2e  |x-x_e|=%9.2e\n", step, (double)t, (double)h, (double)nrm_x, (double)nrm_exact, (double)nrm_diff));
288   PetscFunctionReturn(0);
289 }
290 
291 int main(int argc, char **argv) {
292   PetscFunctionList plist = NULL;
293   char              pname[256];
294   TS                ts;   /* nonlinear solver */
295   Vec               x, r; /* solution, residual vectors */
296   Mat               A;    /* Jacobian matrix */
297   Problem           problem;
298   PetscBool         use_monitor = PETSC_FALSE;
299   PetscBool         use_result  = PETSC_FALSE;
300   PetscInt          steps, nonlinits, linits, snesfails, rejects;
301   PetscReal         ftime;
302   MonitorCtx        mon;
303   PetscMPIInt       size;
304 
305   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
306      Initialize program
307      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
308   PetscFunctionBeginUser;
309   PetscCall(PetscInitialize(&argc, &argv, (char *)0, help));
310   PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
311   PetscCheck(size == 1, PETSC_COMM_WORLD, PETSC_ERR_WRONG_MPI_SIZE, "Only for sequential runs");
312 
313   /* Register the available problems */
314   PetscCall(PetscFunctionListAdd(&plist, "rober", &RoberCreate));
315   PetscCall(PetscFunctionListAdd(&plist, "ce", &CECreate));
316   PetscCall(PetscFunctionListAdd(&plist, "orego", &OregoCreate));
317   PetscCall(PetscStrcpy(pname, "ce"));
318 
319   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
320     Set runtime options
321     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
322   PetscOptionsBegin(PETSC_COMM_WORLD, NULL, "Timestepping benchmark options", "");
323   {
324     PetscCall(PetscOptionsFList("-problem_type", "Name of problem to run", "", plist, pname, pname, sizeof(pname), NULL));
325     use_monitor = PETSC_FALSE;
326     PetscCall(PetscOptionsBool("-monitor_error", "Display errors relative to exact solutions", "", use_monitor, &use_monitor, NULL));
327     PetscCall(PetscOptionsBool("-monitor_result", "Display result", "", use_result, &use_result, NULL));
328   }
329   PetscOptionsEnd();
330 
331   /* Create the new problem */
332   PetscCall(PetscNew(&problem));
333   problem->comm = MPI_COMM_WORLD;
334   {
335     PetscErrorCode (*pcreate)(Problem);
336 
337     PetscCall(PetscFunctionListFind(plist, pname, &pcreate));
338     PetscCheck(pcreate, PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "No problem '%s'", pname);
339     PetscCall((*pcreate)(problem));
340   }
341 
342   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
343     Create necessary matrix and vectors
344     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
345   PetscCall(MatCreate(PETSC_COMM_WORLD, &A));
346   PetscCall(MatSetSizes(A, problem->n, problem->n, PETSC_DETERMINE, PETSC_DETERMINE));
347   PetscCall(MatSetFromOptions(A));
348   PetscCall(MatSetUp(A));
349 
350   PetscCall(MatCreateVecs(A, &x, NULL));
351   PetscCall(VecDuplicate(x, &r));
352 
353   mon.comm    = PETSC_COMM_WORLD;
354   mon.problem = problem;
355   PetscCall(VecDuplicate(x, &mon.x));
356 
357   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
358      Create timestepping solver context
359      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
360   PetscCall(TSCreate(PETSC_COMM_WORLD, &ts));
361   PetscCall(TSSetProblemType(ts, TS_NONLINEAR));
362   PetscCall(TSSetType(ts, TSROSW)); /* Rosenbrock-W */
363   PetscCall(TSSetIFunction(ts, NULL, problem->function, problem->data));
364   PetscCall(TSSetIJacobian(ts, A, A, problem->jacobian, problem->data));
365   PetscCall(TSSetMaxTime(ts, problem->final_time));
366   PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_STEPOVER));
367   PetscCall(TSSetMaxStepRejections(ts, 10));
368   PetscCall(TSSetMaxSNESFailures(ts, -1)); /* unlimited */
369   if (use_monitor) { PetscCall(TSMonitorSet(ts, &MonitorError, &mon, NULL)); }
370 
371   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
372      Set initial conditions
373    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
374   PetscCall((*problem->solution)(0, x, problem->data));
375   PetscCall(TSSetTimeStep(ts, .001));
376   PetscCall(TSSetSolution(ts, x));
377 
378   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
379      Set runtime options
380    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
381   PetscCall(TSSetFromOptions(ts));
382 
383   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
384      Solve nonlinear system
385      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
386   PetscCall(TSSolve(ts, x));
387   PetscCall(TSGetSolveTime(ts, &ftime));
388   PetscCall(TSGetStepNumber(ts, &steps));
389   PetscCall(TSGetSNESFailures(ts, &snesfails));
390   PetscCall(TSGetStepRejections(ts, &rejects));
391   PetscCall(TSGetSNESIterations(ts, &nonlinits));
392   PetscCall(TSGetKSPIterations(ts, &linits));
393   if (use_result) {
394     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "steps %" PetscInt_FMT " (%" PetscInt_FMT " rejected, %" PetscInt_FMT " SNES fails), ftime %g, nonlinits %" PetscInt_FMT ", linits %" PetscInt_FMT "\n", steps, rejects, snesfails, (double)ftime, nonlinits, linits));
395   }
396 
397   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
398      Free work space.  All PETSc objects should be destroyed when they
399      are no longer needed.
400    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
401   PetscCall(MatDestroy(&A));
402   PetscCall(VecDestroy(&x));
403   PetscCall(VecDestroy(&r));
404   PetscCall(VecDestroy(&mon.x));
405   PetscCall(TSDestroy(&ts));
406   if (problem->destroy) PetscCall((*problem->destroy)(problem));
407   PetscCall(PetscFree(problem));
408   PetscCall(PetscFunctionListDestroy(&plist));
409 
410   PetscCall(PetscFinalize());
411   return 0;
412 }
413 
414 /*TEST
415 
416     test:
417       requires: !complex
418       args:  -monitor_result -monitor_error -ts_atol 1e-2 -ts_rtol 1e-2 -ts_exact_final_time interpolate -ts_type arkimex
419 
420     test:
421       suffix: 2
422       requires: !single !complex
423       args: -monitor_result -ts_atol 1e-2 -ts_rtol 1e-2 -ts_max_time 15 -ts_type arkimex -ts_arkimex_type 2e -problem_type orego -ts_arkimex_initial_guess_extrapolate 0 -ts_adapt_time_step_increase_delay 4
424 
425     test:
426       suffix: 3
427       requires: !single !complex
428       args: -monitor_result -ts_atol 1e-2 -ts_rtol 1e-2 -ts_max_time 15 -ts_type arkimex -ts_arkimex_type 2e -problem_type orego -ts_arkimex_initial_guess_extrapolate 1
429 
430     test:
431       suffix: 4
432 
433     test:
434       suffix: 5
435       args: -snes_lag_jacobian 20 -snes_lag_jacobian_persists
436 
437 TEST*/
438