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