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