xref: /petsc/src/ts/tutorials/ex40.c (revision ca4445c7a2f5ca546b532f08b853c371604af17c)
1 static char help[] = "Serial bouncing ball example to test TS event feature.\n";
2 
3 /*
4   The dynamics of the bouncing ball is described by the ODE
5                   u1_t = u2
6                   u2_t = -9.8
7 
8   There are two events set in this example. The first one checks for the ball hitting the
9   ground (u1 = 0). Every time the ball hits the ground, its velocity u2 is attenuated by
10   a factor of 0.9. The second event sets a limit on the number of ball bounces.
11 */
12 
13 #include <petscts.h>
14 
15 typedef struct {
16   PetscInt maxbounces;
17   PetscInt nbounces;
18 } AppCtx;
19 
20 PetscErrorCode EventFunction(TS ts, PetscReal t, Vec U, PetscScalar *fvalue, void *ctx)
21 {
22   AppCtx            *app = (AppCtx *)ctx;
23   const PetscScalar *u;
24 
25   PetscFunctionBeginUser;
26   /* Event for ball height */
27   PetscCall(VecGetArrayRead(U, &u));
28   fvalue[0] = u[0];
29   /* Event for number of bounces */
30   fvalue[1] = app->maxbounces - app->nbounces;
31   PetscCall(VecRestoreArrayRead(U, &u));
32   PetscFunctionReturn(PETSC_SUCCESS);
33 }
34 
35 PetscErrorCode PostEventFunction(TS ts, PetscInt nevents, PetscInt event_list[], PetscReal t, Vec U, PetscBool forwardsolve, void *ctx)
36 {
37   AppCtx      *app = (AppCtx *)ctx;
38   PetscScalar *u;
39 
40   PetscFunctionBeginUser;
41   if (event_list[0] == 0) {
42     PetscCall(PetscPrintf(PETSC_COMM_SELF, "Ball hit the ground at t = %5.2f seconds\n", (double)t));
43     /* Set new initial conditions with .9 attenuation */
44     PetscCall(VecGetArray(U, &u));
45     u[0] = 0.0;
46     u[1] = -0.9 * u[1];
47     PetscCall(VecRestoreArray(U, &u));
48   } else if (event_list[0] == 1) {
49     PetscCall(PetscPrintf(PETSC_COMM_SELF, "Ball bounced %" PetscInt_FMT " times\n", app->nbounces));
50   }
51   app->nbounces++;
52   PetscFunctionReturn(PETSC_SUCCESS);
53 }
54 
55 /*
56      Defines the ODE passed to the ODE solver in explicit form: U_t = F(U)
57 */
58 static PetscErrorCode RHSFunction(TS ts, PetscReal t, Vec U, Vec F, void *ctx)
59 {
60   PetscScalar       *f;
61   const PetscScalar *u;
62 
63   PetscFunctionBeginUser;
64   /*  The next three lines allow us to access the entries of the vectors directly */
65   PetscCall(VecGetArrayRead(U, &u));
66   PetscCall(VecGetArray(F, &f));
67 
68   f[0] = u[1];
69   f[1] = -9.8;
70 
71   PetscCall(VecRestoreArrayRead(U, &u));
72   PetscCall(VecRestoreArray(F, &f));
73   PetscFunctionReturn(PETSC_SUCCESS);
74 }
75 
76 /*
77      Defines the Jacobian of the ODE passed to the ODE solver. See TSSetRHSJacobian() for the meaning of the Jacobian.
78 */
79 static PetscErrorCode RHSJacobian(TS ts, PetscReal t, Vec U, Mat A, Mat B, void *ctx)
80 {
81   PetscInt           rowcol[] = {0, 1};
82   PetscScalar        J[2][2];
83   const PetscScalar *u;
84 
85   PetscFunctionBeginUser;
86   PetscCall(VecGetArrayRead(U, &u));
87 
88   J[0][0] = 0.0;
89   J[0][1] = 1.0;
90   J[1][0] = 0.0;
91   J[1][1] = 0.0;
92   PetscCall(MatSetValues(B, 2, rowcol, 2, rowcol, &J[0][0], INSERT_VALUES));
93 
94   PetscCall(VecRestoreArrayRead(U, &u));
95 
96   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
97   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
98   if (A != B) {
99     PetscCall(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY));
100     PetscCall(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY));
101   }
102   PetscFunctionReturn(PETSC_SUCCESS);
103 }
104 
105 /*
106      Defines the ODE passed to the ODE solver in implicit form: F(U_t,U) = 0
107 */
108 static PetscErrorCode IFunction(TS ts, PetscReal t, Vec U, Vec Udot, Vec F, void *ctx)
109 {
110   PetscScalar       *f;
111   const PetscScalar *u, *udot;
112 
113   PetscFunctionBeginUser;
114   /*  The next three lines allow us to access the entries of the vectors directly */
115   PetscCall(VecGetArrayRead(U, &u));
116   PetscCall(VecGetArrayRead(Udot, &udot));
117   PetscCall(VecGetArray(F, &f));
118 
119   f[0] = udot[0] - u[1];
120   f[1] = udot[1] + 9.8;
121 
122   PetscCall(VecRestoreArrayRead(U, &u));
123   PetscCall(VecRestoreArrayRead(Udot, &udot));
124   PetscCall(VecRestoreArray(F, &f));
125   PetscFunctionReturn(PETSC_SUCCESS);
126 }
127 
128 /*
129      Defines the Jacobian of the ODE passed to the ODE solver. See TSSetIJacobian() for the meaning of a and the Jacobian.
130 */
131 static PetscErrorCode IJacobian(TS ts, PetscReal t, Vec U, Vec Udot, PetscReal a, Mat A, Mat B, void *ctx)
132 {
133   PetscInt           rowcol[] = {0, 1};
134   PetscScalar        J[2][2];
135   const PetscScalar *u, *udot;
136 
137   PetscFunctionBeginUser;
138   PetscCall(VecGetArrayRead(U, &u));
139   PetscCall(VecGetArrayRead(Udot, &udot));
140 
141   J[0][0] = a;
142   J[0][1] = -1.0;
143   J[1][0] = 0.0;
144   J[1][1] = a;
145   PetscCall(MatSetValues(B, 2, rowcol, 2, rowcol, &J[0][0], INSERT_VALUES));
146 
147   PetscCall(VecRestoreArrayRead(U, &u));
148   PetscCall(VecRestoreArrayRead(Udot, &udot));
149 
150   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
151   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
152   if (A != B) {
153     PetscCall(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY));
154     PetscCall(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY));
155   }
156   PetscFunctionReturn(PETSC_SUCCESS);
157 }
158 
159 int main(int argc, char **argv)
160 {
161   TS           ts; /* ODE integrator */
162   Vec          U;  /* solution will be stored here */
163   PetscMPIInt  size;
164   PetscInt     n = 2;
165   PetscScalar *u;
166   AppCtx       app;
167   PetscInt     direction[2];
168   PetscBool    terminate[2];
169   PetscBool    rhs_form = PETSC_FALSE, hist = PETSC_TRUE;
170   TSAdapt      adapt;
171 
172   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
173      Initialize program
174      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
175   PetscFunctionBeginUser;
176   PetscCall(PetscInitialize(&argc, &argv, (char *)0, help));
177   PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
178   PetscCheck(size == 1, PETSC_COMM_WORLD, PETSC_ERR_WRONG_MPI_SIZE, "Only for sequential runs");
179 
180   app.nbounces   = 0;
181   app.maxbounces = 10;
182   PetscOptionsBegin(PETSC_COMM_WORLD, NULL, "ex40 options", "");
183   PetscCall(PetscOptionsInt("-maxbounces", "", "", app.maxbounces, &app.maxbounces, NULL));
184   PetscCall(PetscOptionsBool("-test_adapthistory", "", "", hist, &hist, NULL));
185   PetscOptionsEnd();
186 
187   Mat A; /* Jacobian matrix */
188   PetscCall(MatCreate(PETSC_COMM_WORLD, &A));
189   PetscCall(MatSetSizes(A, n, n, PETSC_DETERMINE, PETSC_DETERMINE));
190   PetscCall(MatSetType(A, MATDENSE));
191   PetscCall(MatSetFromOptions(A));
192   PetscCall(MatSetUp(A));
193   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
194      Create timestepping solver context
195      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
196   PetscCall(TSCreate(PETSC_COMM_WORLD, &ts));
197   PetscCall(TSSetType(ts, TSROSW));
198 
199   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
200      Set ODE routines
201    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
202   PetscCall(TSSetProblemType(ts, TS_NONLINEAR));
203   /* Users are advised against the following branching and code duplication.
204      For problems without a mass matrix like the one at hand, the RHSFunction
205      (and companion RHSJacobian) interface is enough to support both explicit
206      and implicit timesteppers. This tutorial example also deals with the
207      IFunction/IJacobian interface for demonstration and testing purposes. */
208   PetscCall(PetscOptionsGetBool(NULL, NULL, "-rhs-form", &rhs_form, NULL));
209   if (rhs_form) {
210     PetscCall(TSSetRHSFunction(ts, NULL, RHSFunction, NULL));
211     PetscCall(TSSetRHSJacobian(ts, A, A, RHSJacobian, NULL));
212   } else {
213     PetscCall(TSSetIFunction(ts, NULL, IFunction, NULL));
214     PetscCall(TSSetIJacobian(ts, A, A, IJacobian, NULL));
215   }
216 
217   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
218      Set initial conditions
219    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
220   PetscCall(VecCreate(PETSC_COMM_WORLD, &U));
221   PetscCall(VecSetSizes(U, n, PETSC_DETERMINE));
222   PetscCall(VecSetUp(U));
223   PetscCall(VecGetArray(U, &u));
224   u[0] = 0.0;
225   u[1] = 20.0;
226   PetscCall(VecRestoreArray(U, &u));
227   PetscCall(TSSetSolution(ts, U));
228 
229   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
230      Set solver options
231    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
232   if (hist) PetscCall(TSSetSaveTrajectory(ts));
233   PetscCall(TSSetMaxTime(ts, 30.0));
234   PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_STEPOVER));
235   PetscCall(TSSetTimeStep(ts, 0.1));
236   /* The adaptive time step controller could take very large timesteps resulting in
237      the same event occurring multiple times in the same interval. A maximum step size
238      limit is enforced here to avoid this issue. */
239   PetscCall(TSGetAdapt(ts, &adapt));
240   PetscCall(TSAdaptSetStepLimits(adapt, 0.0, 0.5));
241 
242   /* Set directions and terminate flags for the two events */
243   direction[0] = -1;
244   direction[1] = -1;
245   terminate[0] = PETSC_FALSE;
246   terminate[1] = PETSC_TRUE;
247   PetscCall(TSSetEventHandler(ts, 2, direction, terminate, EventFunction, PostEventFunction, (void *)&app));
248 
249   PetscCall(TSSetFromOptions(ts));
250 
251   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
252      Run timestepping solver
253      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
254   PetscCall(TSSolve(ts, U));
255 
256   if (hist) { /* replay following history */
257     TSTrajectory tj;
258     PetscReal    tf, t0, dt;
259 
260     app.nbounces = 0;
261     PetscCall(TSGetTime(ts, &tf));
262     PetscCall(TSSetMaxTime(ts, tf));
263     PetscCall(TSSetStepNumber(ts, 0));
264     PetscCall(TSRestartStep(ts));
265     PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_MATCHSTEP));
266     PetscCall(TSSetFromOptions(ts));
267     PetscCall(TSGetAdapt(ts, &adapt));
268     PetscCall(TSAdaptSetType(adapt, TSADAPTHISTORY));
269     PetscCall(TSGetTrajectory(ts, &tj));
270     PetscCall(TSAdaptHistorySetTrajectory(adapt, tj, PETSC_FALSE));
271     PetscCall(TSAdaptHistoryGetStep(adapt, 0, &t0, &dt));
272     /* this example fails with single (or smaller) precision */
273 #if defined(PETSC_USE_REAL_SINGLE) || defined(PETSC_USE_REAL___FP16)
274     /*
275        In the first TSSolve() the final time 'tf' is the event location found after a few event handler iterations.
276        If 'tf' is set as the max time for the second run, the TS solver may approach this point by
277        slightly different steps, resulting in a slightly different solution and fvalue[] at 'tf',
278        so that the event may not be triggered at 'tf' anymore. Fix: apply safety factor 1.05
279     */
280     PetscCall(TSSetMaxTime(ts, tf * 1.05));
281     PetscCall(TSAdaptSetType(adapt, TSADAPTBASIC));
282     PetscCall(TSAdaptSetStepLimits(adapt, 0.0, 0.5));
283     PetscCall(TSSetFromOptions(ts));
284 #endif
285     PetscCall(TSSetTime(ts, t0));
286     PetscCall(TSSetTimeStep(ts, dt));
287     PetscCall(TSResetTrajectory(ts));
288     PetscCall(VecGetArray(U, &u));
289     u[0] = 0.0;
290     u[1] = 20.0;
291     PetscCall(VecRestoreArray(U, &u));
292     PetscCall(TSSolve(ts, U));
293   }
294   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
295      Free work space.  All PETSc objects should be destroyed when they are no longer needed.
296      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
297   PetscCall(MatDestroy(&A));
298   PetscCall(VecDestroy(&U));
299   PetscCall(TSDestroy(&ts));
300 
301   PetscCall(PetscFinalize());
302   return 0;
303 }
304 
305 /*TEST
306 
307     test:
308       suffix: a
309       args: -snes_stol 1e-4 -ts_trajectory_dirname ex40_a_dir
310       output_file: output/ex40.out
311 
312     test:
313       suffix: b
314       args: -ts_type arkimex -snes_stol 1e-4 -ts_trajectory_dirname ex40_b_dir
315       output_file: output/ex40.out
316 
317     test:
318       suffix: c
319       args: -snes_mf_operator -ts_type theta -ts_adapt_type basic -ts_atol 1e-1 -snes_stol 1e-4 -ts_trajectory_dirname ex40_c_dir
320       output_file: output/ex40.out
321 
322     test:
323       suffix: cr
324       args: -rhs-form -ts_type theta -ts_adapt_type basic -ts_atol 1e-1 -snes_stol 1e-4 -ts_trajectory_dirname ex40_cr_dir
325       output_file: output/ex40.out
326 
327     test:
328       suffix: crmf
329       args: -rhs-form -snes_mf_operator -ts_type theta -ts_adapt_type basic -ts_atol 1e-1 -snes_stol 1e-4 -ts_trajectory_dirname ex40_crmf_dir
330       output_file: output/ex40.out
331 
332     test:
333       suffix: d
334       args: -ts_type alpha -ts_adapt_type basic -ts_atol 1e-1 -snes_stol 1e-4 -ts_trajectory_dirname ex40_d_dir
335       output_file: output/ex40.out
336 
337     test:
338       suffix: e
339       args: -ts_type bdf -ts_adapt_dt_max 0.025 -ts_max_steps 1500 -ts_trajectory_dirname ex40_e_dir
340       output_file: output/ex40.out
341 
342     test:
343       suffix: f
344       args: -rhs-form -ts_type rk -ts_rk_type 3bs -ts_trajectory_dirname ex40_f_dir
345       output_file: output/ex40.out
346 
347     test:
348       suffix: g
349       args: -rhs-form -ts_type rk -ts_rk_type 5bs -ts_trajectory_dirname ex40_g_dir
350       output_file: output/ex40.out
351 
352     test:
353       suffix: h
354       args: -rhs-form -ts_type rk -ts_rk_type 6vr -ts_trajectory_dirname ex40_h_dir
355       output_file: output/ex40.out
356 
357     test:
358       suffix: i
359       args: -rhs-form -ts_type rk -ts_rk_type 7vr -ts_trajectory_dirname ex40_i_dir
360       output_file: output/ex40.out
361 
362     test:
363       suffix: j
364       args: -rhs-form -ts_type rk -ts_rk_type 8vr -ts_trajectory_dirname ex40_j_dir
365       output_file: output/ex40.out
366 
367     test:
368       suffix: k
369       args: -ts_type theta -ts_adapt_type dsp -ts_trajectory_dirname ex40_k_dir
370       output_file: output/ex40.out
371 
372     test:
373       suffix: l
374       args: -rhs-form -ts_type rk -ts_rk_type 2a -ts_trajectory_dirname ex40_l_dir
375       args: -ts_adapt_type dsp -ts_adapt_always_accept {{false true}} -ts_adapt_dt_min 0.01
376       output_file: output/ex40.out
377 
378     test:
379       suffix: m
380       args: -ts_type alpha -ts_adapt_type basic -ts_atol 1e-1 -snes_stol 1e-4 -test_adapthistory false
381       args: -ts_max_time 10 -ts_exact_final_time {{STEPOVER MATCHSTEP INTERPOLATE}}
382 
383     test:
384       requires: !single
385       suffix: n
386       args: -test_adapthistory false
387       args: -ts_type alpha -ts_alpha_radius 1.0 -ts_view
388       args: -ts_dt 0.25 -ts_adapt_type basic -ts_adapt_wnormtype INFINITY -ts_adapt_monitor
389       args: -ts_max_steps 1 -ts_max_reject {{0 1 2}separate_output} -ts_error_if_step_fails false
390 
391     test:
392       requires: !single
393       suffix: o
394       args: -rhs-form -ts_type rk -ts_rk_type 2b -ts_trajectory_dirname ex40_o_dir
395       output_file: output/ex40.out
396 TEST*/
397