1 #include <petsc/private/tsimpl.h> /*I "petscts.h" I*/ 2 3 /* 4 TSEventInitialize - Initializes TSEvent for TSSolve 5 */ 6 PetscErrorCode TSEventInitialize(TSEvent event, TS ts, PetscReal t, Vec U) 7 { 8 PetscFunctionBegin; 9 if (!event) PetscFunctionReturn(PETSC_SUCCESS); 10 PetscValidPointer(event, 1); 11 PetscValidHeaderSpecific(ts, TS_CLASSID, 2); 12 PetscValidHeaderSpecific(U, VEC_CLASSID, 4); 13 event->ptime_prev = t; 14 event->iterctr = 0; 15 PetscCall((*event->eventhandler)(ts, t, U, event->fvalue_prev, event->ctx)); 16 PetscFunctionReturn(PETSC_SUCCESS); 17 } 18 19 PetscErrorCode TSEventDestroy(TSEvent *event) 20 { 21 PetscInt i; 22 23 PetscFunctionBegin; 24 PetscValidPointer(event, 1); 25 if (!*event) PetscFunctionReturn(PETSC_SUCCESS); 26 if (--(*event)->refct > 0) { 27 *event = NULL; 28 PetscFunctionReturn(PETSC_SUCCESS); 29 } 30 31 PetscCall(PetscFree((*event)->fvalue)); 32 PetscCall(PetscFree((*event)->fvalue_prev)); 33 PetscCall(PetscFree((*event)->fvalue_right)); 34 PetscCall(PetscFree((*event)->zerocrossing)); 35 PetscCall(PetscFree((*event)->side)); 36 PetscCall(PetscFree((*event)->direction)); 37 PetscCall(PetscFree((*event)->terminate)); 38 PetscCall(PetscFree((*event)->events_zero)); 39 PetscCall(PetscFree((*event)->vtol)); 40 41 for (i = 0; i < (*event)->recsize; i++) PetscCall(PetscFree((*event)->recorder.eventidx[i])); 42 PetscCall(PetscFree((*event)->recorder.eventidx)); 43 PetscCall(PetscFree((*event)->recorder.nevents)); 44 PetscCall(PetscFree((*event)->recorder.stepnum)); 45 PetscCall(PetscFree((*event)->recorder.time)); 46 47 PetscCall(PetscViewerDestroy(&(*event)->monitor)); 48 PetscCall(PetscFree(*event)); 49 PetscFunctionReturn(PETSC_SUCCESS); 50 } 51 52 /*@ 53 TSSetPostEventIntervalStep - Set the time-step used immediately following an event interval 54 55 Logically Collective 56 57 Input Parameters: 58 + ts - time integration context 59 - dt - post event interval step 60 61 Options Database Key: 62 . -ts_event_post_eventinterval_step <dt> time-step after event interval 63 64 Level: advanced 65 66 Notes: 67 `TSSetPostEventIntervalStep()` allows one to set a time-step that is used immediately following an event interval. 68 69 This function should be called from the postevent function set with `TSSetEventHandler()`. 70 71 The post event interval time-step should be selected based on the dynamics following the event. 72 If the dynamics are stiff, a conservative (small) step should be used. 73 If not, then a larger time-step can be used. 74 75 .seealso: [](chapter_ts), `TS`, `TSEvent`, `TSSetEventHandler()` 76 @*/ 77 PetscErrorCode TSSetPostEventIntervalStep(TS ts, PetscReal dt) 78 { 79 PetscFunctionBegin; 80 ts->event->timestep_posteventinterval = dt; 81 PetscFunctionReturn(PETSC_SUCCESS); 82 } 83 84 /*@ 85 TSSetEventTolerances - Set tolerances for event zero crossings 86 87 Logically Collective 88 89 Input Parameters: 90 + ts - time integration context 91 . tol - scalar tolerance, `PETSC_DECIDE` to leave current value 92 - vtol - array of tolerances or `NULL`, used in preference to tol if present 93 94 Options Database Key: 95 . -ts_event_tol <tol> - tolerance for event zero crossing 96 97 Level: beginner 98 99 Notes: 100 Must call `TSSetEventHandler(`) before setting the tolerances. 101 102 The size of `vtol` is equal to the number of events. 103 104 The tolerance is some measure of how close the event function is to zero for the event detector to stop 105 and declare the time of the event has been detected. 106 107 .seealso: [](chapter_ts), `TS`, `TSEvent`, `TSSetEventHandler()` 108 @*/ 109 PetscErrorCode TSSetEventTolerances(TS ts, PetscReal tol, PetscReal vtol[]) 110 { 111 TSEvent event; 112 PetscInt i; 113 114 PetscFunctionBegin; 115 PetscValidHeaderSpecific(ts, TS_CLASSID, 1); 116 if (vtol) PetscValidRealPointer(vtol, 3); 117 PetscCheck(ts->event, PetscObjectComm((PetscObject)ts), PETSC_ERR_USER, "Must set the events first by calling TSSetEventHandler()"); 118 119 event = ts->event; 120 if (vtol) { 121 for (i = 0; i < event->nevents; i++) event->vtol[i] = vtol[i]; 122 } else { 123 if ((tol != (PetscReal)PETSC_DECIDE) || (tol != (PetscReal)PETSC_DEFAULT)) { 124 for (i = 0; i < event->nevents; i++) event->vtol[i] = tol; 125 } 126 } 127 PetscFunctionReturn(PETSC_SUCCESS); 128 } 129 130 /*@C 131 TSSetEventHandler - Sets a function used for detecting events 132 133 Logically Collective 134 135 Input Parameters: 136 + ts - the `TS` context obtained from `TSCreate()` 137 . nevents - number of local events 138 . direction - direction of zero crossing to be detected. -1 => Zero crossing in negative direction, 139 +1 => Zero crossing in positive direction, 0 => both ways (one for each event) 140 . terminate - flag to indicate whether time stepping should be terminated after 141 event is detected (one for each event) 142 . eventhandler - a change in sign of this function (see `direction`) is used to determine an even has occurred 143 . postevent - [optional] post-event function, this function can change properties of the solution, ODE etc at the time of the event 144 - ctx - [optional] user-defined context for private data for the 145 event detector and post event routine (use `NULL` if no 146 context is desired) 147 148 Calling sequence of `eventhandler`: 149 $ PetscErrorCode eventhandler(TS ts, PetscReal t, Vec U, PetscScalar fvalue[], void* ctx) 150 + ts - the `TS` context 151 . t - current time 152 . U - current iterate 153 . ctx - [optional] context passed with eventhandler 154 - fvalue - function value of events at time t 155 156 Calling sequence of `postevent`: 157 $ PetscErrorCode postevent(TS ts, PetscInt nevents_zero, PetscInt events_zero[], PetscReal t, Vec U, PetscBool forwardsolve, void *ctx) 158 + ts - the `TS` context 159 . nevents_zero - number of local events whose event function is zero 160 . events_zero - indices of local events which have reached zero 161 . t - current time 162 . U - current solution 163 . forwardsolve - Flag to indicate whether `TS` is doing a forward solve (1) or adjoint solve (0) 164 - ctx - the context passed with eventhandler 165 166 Level: intermediate 167 168 Note: 169 The `eventhandler` is actually the event detector function and the `postevent` function actually handles the desired changes that 170 should take place at the time of the event 171 172 .seealso: [](chapter_ts), `TSEvent`, `TSCreate()`, `TSSetTimeStep()`, `TSSetConvergedReason()` 173 @*/ 174 PetscErrorCode TSSetEventHandler(TS ts, PetscInt nevents, PetscInt direction[], PetscBool terminate[], PetscErrorCode (*eventhandler)(TS, PetscReal, Vec, PetscScalar[], void *), PetscErrorCode (*postevent)(TS, PetscInt, PetscInt[], PetscReal, Vec, PetscBool, void *), void *ctx) 175 { 176 TSAdapt adapt; 177 PetscReal hmin; 178 TSEvent event; 179 PetscInt i; 180 PetscBool flg; 181 #if defined PETSC_USE_REAL_SINGLE 182 PetscReal tol = 1e-4; 183 #else 184 PetscReal tol = 1e-6; 185 #endif 186 187 PetscFunctionBegin; 188 PetscValidHeaderSpecific(ts, TS_CLASSID, 1); 189 if (nevents) { 190 PetscValidIntPointer(direction, 3); 191 PetscValidBoolPointer(terminate, 4); 192 } 193 194 PetscCall(PetscNew(&event)); 195 PetscCall(PetscMalloc1(nevents, &event->fvalue)); 196 PetscCall(PetscMalloc1(nevents, &event->fvalue_prev)); 197 PetscCall(PetscMalloc1(nevents, &event->fvalue_right)); 198 PetscCall(PetscMalloc1(nevents, &event->zerocrossing)); 199 PetscCall(PetscMalloc1(nevents, &event->side)); 200 PetscCall(PetscMalloc1(nevents, &event->direction)); 201 PetscCall(PetscMalloc1(nevents, &event->terminate)); 202 PetscCall(PetscMalloc1(nevents, &event->vtol)); 203 for (i = 0; i < nevents; i++) { 204 event->direction[i] = direction[i]; 205 event->terminate[i] = terminate[i]; 206 event->zerocrossing[i] = PETSC_FALSE; 207 event->side[i] = 0; 208 } 209 PetscCall(PetscMalloc1(nevents, &event->events_zero)); 210 event->nevents = nevents; 211 event->eventhandler = eventhandler; 212 event->postevent = postevent; 213 event->ctx = ctx; 214 event->timestep_posteventinterval = ts->time_step; 215 PetscCall(TSGetAdapt(ts, &adapt)); 216 PetscCall(TSAdaptGetStepLimits(adapt, &hmin, NULL)); 217 event->timestep_min = hmin; 218 219 event->recsize = 8; /* Initial size of the recorder */ 220 PetscOptionsBegin(((PetscObject)ts)->comm, ((PetscObject)ts)->prefix, "TS Event options", "TS"); 221 { 222 PetscCall(PetscOptionsReal("-ts_event_tol", "Scalar event tolerance for zero crossing check", "TSSetEventTolerances", tol, &tol, NULL)); 223 PetscCall(PetscOptionsName("-ts_event_monitor", "Print choices made by event handler", "", &flg)); 224 PetscCall(PetscOptionsInt("-ts_event_recorder_initial_size", "Initial size of event recorder", "", event->recsize, &event->recsize, NULL)); 225 PetscCall(PetscOptionsReal("-ts_event_post_eventinterval_step", "Time step after event interval", "", event->timestep_posteventinterval, &event->timestep_posteventinterval, NULL)); 226 PetscCall(PetscOptionsReal("-ts_event_post_event_step", "Time step after event", "", event->timestep_postevent, &event->timestep_postevent, NULL)); 227 PetscCall(PetscOptionsReal("-ts_event_dt_min", "Minimum time step considered for TSEvent", "", event->timestep_min, &event->timestep_min, NULL)); 228 } 229 PetscOptionsEnd(); 230 231 PetscCall(PetscMalloc1(event->recsize, &event->recorder.time)); 232 PetscCall(PetscMalloc1(event->recsize, &event->recorder.stepnum)); 233 PetscCall(PetscMalloc1(event->recsize, &event->recorder.nevents)); 234 PetscCall(PetscMalloc1(event->recsize, &event->recorder.eventidx)); 235 for (i = 0; i < event->recsize; i++) PetscCall(PetscMalloc1(event->nevents, &event->recorder.eventidx[i])); 236 /* Initialize the event recorder */ 237 event->recorder.ctr = 0; 238 239 for (i = 0; i < event->nevents; i++) event->vtol[i] = tol; 240 if (flg) PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &event->monitor)); 241 242 PetscCall(TSEventDestroy(&ts->event)); 243 ts->event = event; 244 ts->event->refct = 1; 245 PetscFunctionReturn(PETSC_SUCCESS); 246 } 247 248 /* 249 TSEventRecorderResize - Resizes (2X) the event recorder arrays whenever the recording limit (event->recsize) 250 is reached. 251 */ 252 static PetscErrorCode TSEventRecorderResize(TSEvent event) 253 { 254 PetscReal *time; 255 PetscInt *stepnum; 256 PetscInt *nevents; 257 PetscInt **eventidx; 258 PetscInt i, fact = 2; 259 260 PetscFunctionBegin; 261 262 /* Create large arrays */ 263 PetscCall(PetscMalloc1(fact * event->recsize, &time)); 264 PetscCall(PetscMalloc1(fact * event->recsize, &stepnum)); 265 PetscCall(PetscMalloc1(fact * event->recsize, &nevents)); 266 PetscCall(PetscMalloc1(fact * event->recsize, &eventidx)); 267 for (i = 0; i < fact * event->recsize; i++) PetscCall(PetscMalloc1(event->nevents, &eventidx[i])); 268 269 /* Copy over data */ 270 PetscCall(PetscArraycpy(time, event->recorder.time, event->recsize)); 271 PetscCall(PetscArraycpy(stepnum, event->recorder.stepnum, event->recsize)); 272 PetscCall(PetscArraycpy(nevents, event->recorder.nevents, event->recsize)); 273 for (i = 0; i < event->recsize; i++) PetscCall(PetscArraycpy(eventidx[i], event->recorder.eventidx[i], event->recorder.nevents[i])); 274 275 /* Destroy old arrays */ 276 for (i = 0; i < event->recsize; i++) PetscCall(PetscFree(event->recorder.eventidx[i])); 277 PetscCall(PetscFree(event->recorder.eventidx)); 278 PetscCall(PetscFree(event->recorder.nevents)); 279 PetscCall(PetscFree(event->recorder.stepnum)); 280 PetscCall(PetscFree(event->recorder.time)); 281 282 /* Set pointers */ 283 event->recorder.time = time; 284 event->recorder.stepnum = stepnum; 285 event->recorder.nevents = nevents; 286 event->recorder.eventidx = eventidx; 287 288 /* Double size */ 289 event->recsize *= fact; 290 291 PetscFunctionReturn(PETSC_SUCCESS); 292 } 293 294 /* 295 Helper routine to handle user postevents and recording 296 */ 297 static PetscErrorCode TSPostEvent(TS ts, PetscReal t, Vec U) 298 { 299 TSEvent event = ts->event; 300 PetscBool terminate = PETSC_FALSE; 301 PetscBool restart = PETSC_FALSE; 302 PetscInt i, ctr, stepnum; 303 PetscBool inflag[2], outflag[2]; 304 PetscBool forwardsolve = PETSC_TRUE; /* Flag indicating that TS is doing a forward solve */ 305 306 PetscFunctionBegin; 307 if (event->postevent) { 308 PetscObjectState state_prev, state_post; 309 PetscCall(PetscObjectStateGet((PetscObject)U, &state_prev)); 310 PetscCall((*event->postevent)(ts, event->nevents_zero, event->events_zero, t, U, forwardsolve, event->ctx)); 311 PetscCall(PetscObjectStateGet((PetscObject)U, &state_post)); 312 if (state_prev != state_post) restart = PETSC_TRUE; 313 } 314 315 /* Handle termination events and step restart */ 316 for (i = 0; i < event->nevents_zero; i++) 317 if (event->terminate[event->events_zero[i]]) terminate = PETSC_TRUE; 318 inflag[0] = restart; 319 inflag[1] = terminate; 320 PetscCall(MPIU_Allreduce(inflag, outflag, 2, MPIU_BOOL, MPI_LOR, ((PetscObject)ts)->comm)); 321 restart = outflag[0]; 322 terminate = outflag[1]; 323 if (restart) PetscCall(TSRestartStep(ts)); 324 if (terminate) PetscCall(TSSetConvergedReason(ts, TS_CONVERGED_EVENT)); 325 event->status = terminate ? TSEVENT_NONE : TSEVENT_RESET_NEXTSTEP; 326 327 /* Reset event residual functions as states might get changed by the postevent callback */ 328 if (event->postevent) { 329 PetscCall(VecLockReadPush(U)); 330 PetscCall((*event->eventhandler)(ts, t, U, event->fvalue, event->ctx)); 331 PetscCall(VecLockReadPop(U)); 332 } 333 334 /* Cache current time and event residual functions */ 335 event->ptime_prev = t; 336 for (i = 0; i < event->nevents; i++) event->fvalue_prev[i] = event->fvalue[i]; 337 338 /* Record the event in the event recorder */ 339 PetscCall(TSGetStepNumber(ts, &stepnum)); 340 ctr = event->recorder.ctr; 341 if (ctr == event->recsize) PetscCall(TSEventRecorderResize(event)); 342 event->recorder.time[ctr] = t; 343 event->recorder.stepnum[ctr] = stepnum; 344 event->recorder.nevents[ctr] = event->nevents_zero; 345 for (i = 0; i < event->nevents_zero; i++) event->recorder.eventidx[ctr][i] = event->events_zero[i]; 346 event->recorder.ctr++; 347 PetscFunctionReturn(PETSC_SUCCESS); 348 } 349 350 /* Uses Anderson-Bjorck variant of regula falsi method */ 351 static inline PetscReal TSEventComputeStepSize(PetscReal tleft, PetscReal t, PetscReal tright, PetscScalar fleft, PetscScalar f, PetscScalar fright, PetscInt side, PetscReal dt) 352 { 353 PetscReal new_dt, scal = 1.0; 354 if (PetscRealPart(fleft) * PetscRealPart(f) < 0) { 355 if (side == 1) { 356 scal = (PetscRealPart(fright) - PetscRealPart(f)) / PetscRealPart(fright); 357 if (scal < PETSC_SMALL) scal = 0.5; 358 } 359 new_dt = (scal * PetscRealPart(fleft) * t - PetscRealPart(f) * tleft) / (scal * PetscRealPart(fleft) - PetscRealPart(f)) - tleft; 360 } else { 361 if (side == -1) { 362 scal = (PetscRealPart(fleft) - PetscRealPart(f)) / PetscRealPart(fleft); 363 if (scal < PETSC_SMALL) scal = 0.5; 364 } 365 new_dt = (PetscRealPart(f) * tright - scal * PetscRealPart(fright) * t) / (PetscRealPart(f) - scal * PetscRealPart(fright)) - t; 366 } 367 return PetscMin(dt, new_dt); 368 } 369 370 static PetscErrorCode TSEventDetection(TS ts) 371 { 372 TSEvent event = ts->event; 373 PetscReal t; 374 PetscInt i; 375 PetscInt fvalue_sign, fvalueprev_sign; 376 PetscInt in, out; 377 378 PetscFunctionBegin; 379 PetscCall(TSGetTime(ts, &t)); 380 for (i = 0; i < event->nevents; i++) { 381 if (PetscAbsScalar(event->fvalue[i]) < event->vtol[i]) { 382 if (!event->iterctr) event->zerocrossing[i] = PETSC_TRUE; 383 event->status = TSEVENT_LOCATED_INTERVAL; 384 if (event->monitor) { 385 PetscCall(PetscViewerASCIIPrintf(event->monitor, "TSEvent: iter %" PetscInt_FMT " - Event %" PetscInt_FMT " interval detected due to zero value (tol=%g) [%g - %g]\n", event->iterctr, i, (double)event->vtol[i], (double)event->ptime_prev, (double)t)); 386 } 387 continue; 388 } 389 if (PetscAbsScalar(event->fvalue_prev[i]) < event->vtol[i]) continue; /* avoid duplicative detection if the previous endpoint is an event location */ 390 fvalue_sign = PetscSign(PetscRealPart(event->fvalue[i])); 391 fvalueprev_sign = PetscSign(PetscRealPart(event->fvalue_prev[i])); 392 if (fvalueprev_sign != 0 && (fvalue_sign != fvalueprev_sign)) { 393 if (!event->iterctr) event->zerocrossing[i] = PETSC_TRUE; 394 event->status = TSEVENT_LOCATED_INTERVAL; 395 if (event->monitor) PetscCall(PetscViewerASCIIPrintf(event->monitor, "TSEvent: iter %" PetscInt_FMT " - Event %" PetscInt_FMT " interval detected due to sign change [%g - %g]\n", event->iterctr, i, (double)event->ptime_prev, (double)t)); 396 } 397 } 398 in = (PetscInt)event->status; 399 PetscCall(MPIU_Allreduce(&in, &out, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)ts))); 400 event->status = (TSEventStatus)out; 401 PetscFunctionReturn(PETSC_SUCCESS); 402 } 403 404 static PetscErrorCode TSEventLocation(TS ts, PetscReal *dt) 405 { 406 TSEvent event = ts->event; 407 PetscInt i; 408 PetscReal t; 409 PetscInt fvalue_sign, fvalueprev_sign; 410 PetscInt rollback = 0, in[2], out[2]; 411 412 PetscFunctionBegin; 413 PetscCall(TSGetTime(ts, &t)); 414 event->nevents_zero = 0; 415 for (i = 0; i < event->nevents; i++) { 416 if (event->zerocrossing[i]) { 417 if (PetscAbsScalar(event->fvalue[i]) < event->vtol[i] || *dt < event->timestep_min || PetscAbsReal((*dt) / ((event->ptime_right - event->ptime_prev) / 2)) < event->vtol[i]) { /* stopping criteria */ 418 event->status = TSEVENT_ZERO; 419 event->fvalue_right[i] = event->fvalue[i]; 420 continue; 421 } 422 /* Compute new time step */ 423 *dt = TSEventComputeStepSize(event->ptime_prev, t, event->ptime_right, event->fvalue_prev[i], event->fvalue[i], event->fvalue_right[i], event->side[i], *dt); 424 fvalue_sign = PetscSign(PetscRealPart(event->fvalue[i])); 425 fvalueprev_sign = PetscSign(PetscRealPart(event->fvalue_prev[i])); 426 switch (event->direction[i]) { 427 case -1: 428 if (fvalue_sign < 0) { 429 rollback = 1; 430 event->fvalue_right[i] = event->fvalue[i]; 431 event->side[i] = 1; 432 } 433 break; 434 case 1: 435 if (fvalue_sign > 0) { 436 rollback = 1; 437 event->fvalue_right[i] = event->fvalue[i]; 438 event->side[i] = 1; 439 } 440 break; 441 case 0: 442 if (fvalue_sign != fvalueprev_sign) { /* trigger rollback only when there is a sign change */ 443 rollback = 1; 444 event->fvalue_right[i] = event->fvalue[i]; 445 event->side[i] = 1; 446 } 447 break; 448 } 449 if (event->status == TSEVENT_PROCESSING) event->side[i] = -1; 450 } 451 } 452 in[0] = (PetscInt)event->status; 453 in[1] = rollback; 454 PetscCall(MPIU_Allreduce(in, out, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)ts))); 455 event->status = (TSEventStatus)out[0]; 456 rollback = out[1]; 457 /* If rollback is true, the status will be overwritten so that an event at the endtime of current time step will be postponed to guarantee correct order */ 458 if (rollback) event->status = TSEVENT_LOCATED_INTERVAL; 459 if (event->status == TSEVENT_ZERO) { 460 for (i = 0; i < event->nevents; i++) { 461 if (event->zerocrossing[i]) { 462 if (PetscAbsScalar(event->fvalue[i]) < event->vtol[i] || *dt < event->timestep_min || PetscAbsReal((*dt) / ((event->ptime_right - event->ptime_prev) / 2)) < event->vtol[i]) { /* stopping criteria */ 463 event->events_zero[event->nevents_zero++] = i; 464 if (event->monitor) PetscCall(PetscViewerASCIIPrintf(event->monitor, "TSEvent: iter %" PetscInt_FMT " - Event %" PetscInt_FMT " zero crossing located at time %g\n", event->iterctr, i, (double)t)); 465 event->zerocrossing[i] = PETSC_FALSE; 466 } 467 } 468 event->side[i] = 0; 469 } 470 } 471 PetscFunctionReturn(PETSC_SUCCESS); 472 } 473 474 PetscErrorCode TSEventHandler(TS ts) 475 { 476 TSEvent event; 477 PetscReal t; 478 Vec U; 479 PetscInt i; 480 PetscReal dt, dt_min, dt_reset = 0.0; 481 482 PetscFunctionBegin; 483 PetscValidHeaderSpecific(ts, TS_CLASSID, 1); 484 if (!ts->event) PetscFunctionReturn(PETSC_SUCCESS); 485 event = ts->event; 486 487 PetscCall(TSGetTime(ts, &t)); 488 PetscCall(TSGetTimeStep(ts, &dt)); 489 PetscCall(TSGetSolution(ts, &U)); 490 491 if (event->status == TSEVENT_NONE) { 492 event->timestep_prev = dt; 493 event->ptime_end = t; 494 } 495 if (event->status == TSEVENT_RESET_NEXTSTEP) { 496 /* user has specified a PostEventInterval dt */ 497 dt = event->timestep_posteventinterval; 498 if (ts->exact_final_time == TS_EXACTFINALTIME_MATCHSTEP) { 499 PetscReal maxdt = ts->max_time - t; 500 dt = dt > maxdt ? maxdt : (PetscIsCloseAtTol(dt, maxdt, 10 * PETSC_MACHINE_EPSILON, 0) ? maxdt : dt); 501 } 502 PetscCall(TSSetTimeStep(ts, dt)); 503 event->status = TSEVENT_NONE; 504 } 505 506 PetscCall(VecLockReadPush(U)); 507 PetscCall((*event->eventhandler)(ts, t, U, event->fvalue, event->ctx)); 508 PetscCall(VecLockReadPop(U)); 509 510 /* Detect the events */ 511 PetscCall(TSEventDetection(ts)); 512 513 /* Locate the events */ 514 if (event->status == TSEVENT_LOCATED_INTERVAL || event->status == TSEVENT_PROCESSING) { 515 /* Approach the zero crosing by setting a new step size */ 516 PetscCall(TSEventLocation(ts, &dt)); 517 /* Roll back when new events are detected */ 518 if (event->status == TSEVENT_LOCATED_INTERVAL) { 519 PetscCall(TSRollBack(ts)); 520 PetscCall(TSSetConvergedReason(ts, TS_CONVERGED_ITERATING)); 521 event->iterctr++; 522 } 523 PetscCall(MPIU_Allreduce(&dt, &dt_min, 1, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)ts))); 524 if (dt_reset > 0.0 && dt_reset < dt_min) dt_min = dt_reset; 525 PetscCall(TSSetTimeStep(ts, dt_min)); 526 /* Found the zero crossing */ 527 if (event->status == TSEVENT_ZERO) { 528 PetscCall(TSPostEvent(ts, t, U)); 529 530 dt = event->ptime_end - t; 531 if (PetscAbsReal(dt) < PETSC_SMALL) { /* we hit the event, continue with the candidate time step */ 532 dt = event->timestep_prev; 533 event->status = TSEVENT_NONE; 534 } 535 if (event->timestep_postevent) { /* user has specified a PostEvent dt*/ 536 dt = event->timestep_postevent; 537 } 538 if (ts->exact_final_time == TS_EXACTFINALTIME_MATCHSTEP) { 539 PetscReal maxdt = ts->max_time - t; 540 dt = dt > maxdt ? maxdt : (PetscIsCloseAtTol(dt, maxdt, 10 * PETSC_MACHINE_EPSILON, 0) ? maxdt : dt); 541 } 542 PetscCall(TSSetTimeStep(ts, dt)); 543 event->iterctr = 0; 544 } 545 /* Have not found the zero crosing yet */ 546 if (event->status == TSEVENT_PROCESSING) { 547 if (event->monitor) PetscCall(PetscViewerASCIIPrintf(event->monitor, "TSEvent: iter %" PetscInt_FMT " - Stepping forward as no event detected in interval [%g - %g]\n", event->iterctr, (double)event->ptime_prev, (double)t)); 548 event->iterctr++; 549 } 550 } 551 if (event->status == TSEVENT_LOCATED_INTERVAL) { /* The step has been rolled back */ 552 event->status = TSEVENT_PROCESSING; 553 event->ptime_right = t; 554 } else { 555 for (i = 0; i < event->nevents; i++) event->fvalue_prev[i] = event->fvalue[i]; 556 event->ptime_prev = t; 557 } 558 PetscFunctionReturn(PETSC_SUCCESS); 559 } 560 561 PetscErrorCode TSAdjointEventHandler(TS ts) 562 { 563 TSEvent event; 564 PetscReal t; 565 Vec U; 566 PetscInt ctr; 567 PetscBool forwardsolve = PETSC_FALSE; /* Flag indicating that TS is doing an adjoint solve */ 568 569 PetscFunctionBegin; 570 PetscValidHeaderSpecific(ts, TS_CLASSID, 1); 571 if (!ts->event) PetscFunctionReturn(PETSC_SUCCESS); 572 event = ts->event; 573 574 PetscCall(TSGetTime(ts, &t)); 575 PetscCall(TSGetSolution(ts, &U)); 576 577 ctr = event->recorder.ctr - 1; 578 if (ctr >= 0 && PetscAbsReal(t - event->recorder.time[ctr]) < PETSC_SMALL) { 579 /* Call the user postevent function */ 580 if (event->postevent) { 581 PetscCall((*event->postevent)(ts, event->recorder.nevents[ctr], event->recorder.eventidx[ctr], t, U, forwardsolve, event->ctx)); 582 event->recorder.ctr--; 583 } 584 } 585 586 PetscCall(PetscBarrier((PetscObject)ts)); 587 PetscFunctionReturn(PETSC_SUCCESS); 588 } 589 590 /*@ 591 TSGetNumEvents - Get the numbers of events currently set to be detected 592 593 Logically Collective 594 595 Input Parameter: 596 . ts - the `TS` context 597 598 Output Parameter: 599 . nevents - the number of events 600 601 Level: intermediate 602 603 .seealso: [](chapter_ts), `TSEvent`, `TSSetEventHandler()` 604 @*/ 605 PetscErrorCode TSGetNumEvents(TS ts, PetscInt *nevents) 606 { 607 PetscFunctionBegin; 608 *nevents = ts->event->nevents; 609 PetscFunctionReturn(PETSC_SUCCESS); 610 } 611