xref: /petsc/src/ts/event/tsevent.c (revision c77c71ff2d9eaa2c74538bf9bf94eff01b512dbf) !
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