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