1af0996ceSBarry Smith #include <petsc/private/tsimpl.h> /*I "petscts.h" I*/ 22dc7a7e3SShri Abhyankar 36fea3669SShri Abhyankar /* 4ca4445c7SIlya Fursov Fills array sign[] with signs of array f[]. If abs(f[i]) < vtol[i], the zero sign is taken. 5ca4445c7SIlya Fursov All arrays should have length 'nev' 6ca4445c7SIlya Fursov */ 7ca4445c7SIlya Fursov static inline void TSEventCalcSigns(PetscInt nev, const PetscReal *f, const PetscReal *vtol, PetscInt *sign) 8ca4445c7SIlya Fursov { 9ca4445c7SIlya Fursov for (PetscInt i = 0; i < nev; i++) { 10ca4445c7SIlya Fursov if (PetscAbsReal(f[i]) < vtol[i]) sign[i] = 0; 11ca4445c7SIlya Fursov else sign[i] = PetscSign(f[i]); 12ca4445c7SIlya Fursov } 13ca4445c7SIlya Fursov } 14ca4445c7SIlya Fursov 15ca4445c7SIlya Fursov /* 166427ac75SLisandro Dalcin TSEventInitialize - Initializes TSEvent for TSSolve 176fea3669SShri Abhyankar */ 18d71ae5a4SJacob Faibussowitsch PetscErrorCode TSEventInitialize(TSEvent event, TS ts, PetscReal t, Vec U) 19d71ae5a4SJacob Faibussowitsch { 206fea3669SShri Abhyankar PetscFunctionBegin; 213ba16761SJacob Faibussowitsch if (!event) PetscFunctionReturn(PETSC_SUCCESS); 224f572ea9SToby Isaac PetscAssertPointer(event, 1); 236427ac75SLisandro Dalcin PetscValidHeaderSpecific(ts, TS_CLASSID, 2); 246427ac75SLisandro Dalcin PetscValidHeaderSpecific(U, VEC_CLASSID, 4); 256fea3669SShri Abhyankar event->ptime_prev = t; 2638bf2713SShri Abhyankar event->iterctr = 0; 27ca4445c7SIlya Fursov event->processing = PETSC_FALSE; 28ca4445c7SIlya Fursov event->revisit_right = PETSC_FALSE; 29ca4445c7SIlya Fursov PetscCallBack("TSEvent indicator", (*event->indicator)(ts, t, U, event->fvalue_prev, event->ctx)); 30ca4445c7SIlya Fursov TSEventCalcSigns(event->nevents, event->fvalue_prev, event->vtol, event->fsign_prev); // by this time event->vtol should have been defined 313ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 326fea3669SShri Abhyankar } 336fea3669SShri Abhyankar 34d71ae5a4SJacob Faibussowitsch PetscErrorCode TSEventDestroy(TSEvent *event) 35d71ae5a4SJacob Faibussowitsch { 367dbe0728SLisandro Dalcin PetscFunctionBegin; 374f572ea9SToby Isaac PetscAssertPointer(event, 1); 383ba16761SJacob Faibussowitsch if (!*event) PetscFunctionReturn(PETSC_SUCCESS); 399371c9d4SSatish Balay if (--(*event)->refct > 0) { 409371c9d4SSatish Balay *event = NULL; 413ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 429371c9d4SSatish Balay } 43e7069c78SShri 449566063dSJacob Faibussowitsch PetscCall(PetscFree((*event)->fvalue_prev)); 45ca4445c7SIlya Fursov PetscCall(PetscFree((*event)->fvalue)); 469566063dSJacob Faibussowitsch PetscCall(PetscFree((*event)->fvalue_right)); 47ca4445c7SIlya Fursov PetscCall(PetscFree((*event)->fsign_prev)); 48ca4445c7SIlya Fursov PetscCall(PetscFree((*event)->fsign)); 49ca4445c7SIlya Fursov PetscCall(PetscFree((*event)->fsign_right)); 509566063dSJacob Faibussowitsch PetscCall(PetscFree((*event)->side)); 51ca4445c7SIlya Fursov PetscCall(PetscFree((*event)->side_prev)); 52ca4445c7SIlya Fursov PetscCall(PetscFree((*event)->justrefined_AB)); 53ca4445c7SIlya Fursov PetscCall(PetscFree((*event)->gamma_AB)); 549566063dSJacob Faibussowitsch PetscCall(PetscFree((*event)->direction)); 559566063dSJacob Faibussowitsch PetscCall(PetscFree((*event)->terminate)); 569566063dSJacob Faibussowitsch PetscCall(PetscFree((*event)->events_zero)); 579566063dSJacob Faibussowitsch PetscCall(PetscFree((*event)->vtol)); 58a4ffd976SShri Abhyankar 59ca4445c7SIlya Fursov for (PetscInt i = 0; i < (*event)->recsize; i++) PetscCall(PetscFree((*event)->recorder.eventidx[i])); 609566063dSJacob Faibussowitsch PetscCall(PetscFree((*event)->recorder.eventidx)); 619566063dSJacob Faibussowitsch PetscCall(PetscFree((*event)->recorder.nevents)); 629566063dSJacob Faibussowitsch PetscCall(PetscFree((*event)->recorder.stepnum)); 639566063dSJacob Faibussowitsch PetscCall(PetscFree((*event)->recorder.time)); 64a4ffd976SShri Abhyankar 659566063dSJacob Faibussowitsch PetscCall(PetscViewerDestroy(&(*event)->monitor)); 669566063dSJacob Faibussowitsch PetscCall(PetscFree(*event)); 673ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 687dbe0728SLisandro Dalcin } 697dbe0728SLisandro Dalcin 70*fe4ad979SIlya Fursov // PetscClangLinter pragma disable: -fdoc-section-header-unknown 71e3005195SShri Abhyankar /*@ 72*fe4ad979SIlya Fursov TSSetPostEventStep - Set the first time step to use after the event 73ca4445c7SIlya Fursov 74ca4445c7SIlya Fursov Logically Collective 75ca4445c7SIlya Fursov 76ca4445c7SIlya Fursov Input Parameters: 77ca4445c7SIlya Fursov + ts - time integration context 78*fe4ad979SIlya Fursov - dt1 - first post event step 79ca4445c7SIlya Fursov 80ca4445c7SIlya Fursov Options Database Key: 81*fe4ad979SIlya Fursov . -ts_event_post_event_step <dt1> - first time step after the event 82ca4445c7SIlya Fursov 83ca4445c7SIlya Fursov Level: advanced 84ca4445c7SIlya Fursov 85ca4445c7SIlya Fursov Notes: 86*fe4ad979SIlya Fursov `TSSetPostEventStep()` allows one to set a time step to use immediately following an event. 87*fe4ad979SIlya Fursov Note, if `TSAdapt` is allowed to interfere and reject steps, a large 'dt1' set by `TSSetPostEventStep()` may get truncated, 88*fe4ad979SIlya Fursov resulting in a smaller actual post-event step. See also the warning below regarding the `TSAdapt`. 89ca4445c7SIlya Fursov 90*fe4ad979SIlya Fursov The post-event time steps should be selected based on the post-event dynamics. 91ca4445c7SIlya Fursov If the dynamics are stiff, or a significant jump in the equations or the state vector has taken place at the event, 92*fe4ad979SIlya Fursov conservative (small) steps should be employed. If not, then larger time steps may be appropriate. 93ca4445c7SIlya Fursov 94*fe4ad979SIlya Fursov This function accepts either a numerical value for `dt1`, or `PETSC_DECIDE`. The special value `PETSC_DECIDE` signals the event handler to follow 95*fe4ad979SIlya Fursov the originally planned trajectory, and is assumed by default. 96*fe4ad979SIlya Fursov 97*fe4ad979SIlya Fursov To describe the way `PETSC_DECIDE` affects the post-event steps, consider a trajectory of time points t1 -> t2 -> t3 -> t4. 98*fe4ad979SIlya Fursov Suppose the TS has reached and calculated the solution at point t3, and has planned the next move: t3 -> t4. 99*fe4ad979SIlya Fursov At this moment, an event between t2 and t3 is detected, and after a few iterations it is resolved at point `te`, t2 < te < t3. 100*fe4ad979SIlya Fursov After event `te`, two post-event steps can be specified: the first one dt1 (`TSSetPostEventStep()`), 101*fe4ad979SIlya Fursov and the second one dt2 (`TSSetPostEventSecondStep()`). Both post-event steps can be either `PETSC_DECIDE`, or a number. 102*fe4ad979SIlya Fursov Four different combinations are possible\: 103*fe4ad979SIlya Fursov 104*fe4ad979SIlya Fursov 1. dt1 = `PETSC_DECIDE`, dt2 = `PETSC_DECIDE`. Then, after `te` TS goes to t3, and then to t4. This is the all-default behaviour. 105*fe4ad979SIlya Fursov 106*fe4ad979SIlya Fursov 2. dt1 = `PETSC_DECIDE`, dt2 = x2 (numerical). Then, after `te` TS goes to t3, and then to t3+x2. 107*fe4ad979SIlya Fursov 108*fe4ad979SIlya Fursov 3. dt1 = x1 (numerical), dt2 = x2 (numerical). Then, after `te` TS goes to te+x1, and then to te+x1+x2. 109*fe4ad979SIlya Fursov 110*fe4ad979SIlya Fursov 4. dt1 = x1 (numerical), dt2 = `PETSC_DECIDE`. Then, after `te` TS goes to te+x1, and event handler does not interfere to the subsequent steps. 111*fe4ad979SIlya Fursov 112*fe4ad979SIlya Fursov In the special case when `te` == t3 with a good precision, the post-event step te -> t3 is not performed, so behaviour of (1) and (2) becomes\: 113*fe4ad979SIlya Fursov 114*fe4ad979SIlya Fursov 1a. After `te` TS goes to t4, and event handler does not interfere to the subsequent steps. 115*fe4ad979SIlya Fursov 116*fe4ad979SIlya Fursov 2a. After `te` TS goes to t4, and then to t4+x2. 117*fe4ad979SIlya Fursov 118*fe4ad979SIlya Fursov Warning! When the second post-event step (either PETSC_DECIDE or a numerical value) is managed by the event handler, i.e. in cases 1, 2, 3 and 2a, 119*fe4ad979SIlya Fursov `TSAdapt` will never analyse (and never do a reasonable rejection of) the first post-event step. The first post-event step will always be accepted. 120*fe4ad979SIlya Fursov In this situation, it is the user's responsibility to make sure the step size is appropriate! 121*fe4ad979SIlya Fursov In cases 4 and 1a, however, `TSAdapt` will analyse the first post-event step, and is allowed to reject it. 122ca4445c7SIlya Fursov 123ca4445c7SIlya Fursov This function can be called not only in the initial setup, but also inside the `postevent()` callback set with `TSSetEventHandler()`, 124*fe4ad979SIlya Fursov affecting the post-event steps for the current event, and the subsequent ones. 125*fe4ad979SIlya Fursov Thus, the strategy of the post-event time step definition can be adjusted on the fly. 126*fe4ad979SIlya Fursov In case several events are triggered in the given time point, only a single postevent handler is invoked, 127ca4445c7SIlya Fursov and the user is to determine what post-event time step is more appropriate in this situation. 128ca4445c7SIlya Fursov 129*fe4ad979SIlya Fursov The default value is `PETSC_DECIDE`. 130ca4445c7SIlya Fursov 131*fe4ad979SIlya Fursov Developer Notes: 132*fe4ad979SIlya Fursov Event processing starts after visiting point t3, which means ts->adapt->dt_span_cached has been set to whatever value is required 133*fe4ad979SIlya Fursov when planning the step t3 -> t4. 134*fe4ad979SIlya Fursov 135*fe4ad979SIlya Fursov .seealso: [](ch_ts), `TS`, `TSEvent`, `TSSetEventHandler()`, `TSSetPostEventSecondStep()` 136ca4445c7SIlya Fursov @*/ 137*fe4ad979SIlya Fursov PetscErrorCode TSSetPostEventStep(TS ts, PetscReal dt1) 138ca4445c7SIlya Fursov { 139ca4445c7SIlya Fursov PetscFunctionBegin; 140*fe4ad979SIlya Fursov ts->event->timestep_postevent = dt1; 141ca4445c7SIlya Fursov PetscFunctionReturn(PETSC_SUCCESS); 142ca4445c7SIlya Fursov } 143ca4445c7SIlya Fursov 144*fe4ad979SIlya Fursov // PetscClangLinter pragma disable: -fdoc-section-header-unknown 145ca4445c7SIlya Fursov /*@ 146*fe4ad979SIlya Fursov TSSetPostEventSecondStep - Set the second time step to use after the event 147458122a4SShri Abhyankar 148458122a4SShri Abhyankar Logically Collective 149458122a4SShri Abhyankar 1504165533cSJose E. Roman Input Parameters: 151458122a4SShri Abhyankar + ts - time integration context 152*fe4ad979SIlya Fursov - dt2 - second post event step 153458122a4SShri Abhyankar 154bcf0153eSBarry Smith Options Database Key: 155*fe4ad979SIlya Fursov . -ts_event_post_event_second_step <dt2> - second time step after the event 156458122a4SShri Abhyankar 157bcf0153eSBarry Smith Level: advanced 158458122a4SShri Abhyankar 159bcf0153eSBarry Smith Notes: 160*fe4ad979SIlya Fursov `TSSetPostEventSecondStep()` allows one to set the second time step after the event. 161458122a4SShri Abhyankar 162*fe4ad979SIlya Fursov The post-event time steps should be selected based on the post-event dynamics. 163*fe4ad979SIlya Fursov If the dynamics are stiff, or a significant jump in the equations or the state vector has taken place at the event, 164*fe4ad979SIlya Fursov conservative (small) steps should be employed. If not, then larger time steps may be appropriate. 165*fe4ad979SIlya Fursov 166*fe4ad979SIlya Fursov This function accepts either a numerical value for `dt2`, or `PETSC_DECIDE` (default). 167*fe4ad979SIlya Fursov 168*fe4ad979SIlya Fursov To describe the way `PETSC_DECIDE` affects the post-event steps, consider a trajectory of time points t1 -> t2 -> t3 -> t4. 169*fe4ad979SIlya Fursov Suppose the TS has reached and calculated the solution at point t3, and has planned the next move: t3 -> t4. 170*fe4ad979SIlya Fursov At this moment, an event between t2 and t3 is detected, and after a few iterations it is resolved at point `te`, t2 < te < t3. 171*fe4ad979SIlya Fursov After event `te`, two post-event steps can be specified: the first one dt1 (`TSSetPostEventStep()`), 172*fe4ad979SIlya Fursov and the second one dt2 (`TSSetPostEventSecondStep()`). Both post-event steps can be either `PETSC_DECIDE`, or a number. 173*fe4ad979SIlya Fursov Four different combinations are possible\: 174*fe4ad979SIlya Fursov 175*fe4ad979SIlya Fursov 1. dt1 = `PETSC_DECIDE`, dt2 = `PETSC_DECIDE`. Then, after `te` TS goes to t3, and then to t4. This is the all-default behaviour. 176*fe4ad979SIlya Fursov 177*fe4ad979SIlya Fursov 2. dt1 = `PETSC_DECIDE`, dt2 = x2 (numerical). Then, after `te` TS goes to t3, and then to t3+x2. 178*fe4ad979SIlya Fursov 179*fe4ad979SIlya Fursov 3. dt1 = x1 (numerical), dt2 = x2 (numerical). Then, after `te` TS goes to te+x1, and then to te+x1+x2. 180*fe4ad979SIlya Fursov 181*fe4ad979SIlya Fursov 4. dt1 = x1 (numerical), dt2 = `PETSC_DECIDE`. Then, after `te` TS goes to te+x1, and event handler does not interfere to the subsequent steps. 182*fe4ad979SIlya Fursov 183*fe4ad979SIlya Fursov In the special case when `te` == t3 with a good precision, the post-event step te -> t3 is not performed, so behaviour of (1) and (2) becomes\: 184*fe4ad979SIlya Fursov 185*fe4ad979SIlya Fursov 1a. After `te` TS goes to t4, and event handler does not interfere to the subsequent steps. 186*fe4ad979SIlya Fursov 187*fe4ad979SIlya Fursov 2a. After `te` TS goes to t4, and then to t4+x2. 188*fe4ad979SIlya Fursov 189*fe4ad979SIlya Fursov Warning! When the second post-event step (either PETSC_DECIDE or a numerical value) is managed by the event handler, i.e. in cases 1, 2, 3 and 2a, 190*fe4ad979SIlya Fursov `TSAdapt` will never analyse (and never do a reasonable rejection of) the first post-event step. The first post-event step will always be accepted. 191*fe4ad979SIlya Fursov In this situation, it is the user's responsibility to make sure the step size is appropriate! 192*fe4ad979SIlya Fursov In cases 4 and 1a, however, `TSAdapt` will analyse the first post-event step, and is allowed to reject it. 193*fe4ad979SIlya Fursov 194*fe4ad979SIlya Fursov This function can be called not only in the initial setup, but also inside the `postevent()` callback set with `TSSetEventHandler()`, 195*fe4ad979SIlya Fursov affecting the post-event steps for the current event, and the subsequent ones. 196*fe4ad979SIlya Fursov 197*fe4ad979SIlya Fursov The default value is `PETSC_DECIDE`. 198*fe4ad979SIlya Fursov 199*fe4ad979SIlya Fursov .seealso: [](ch_ts), `TS`, `TSEvent`, `TSSetEventHandler()`, `TSSetPostEventStep()` 200458122a4SShri Abhyankar @*/ 201*fe4ad979SIlya Fursov PetscErrorCode TSSetPostEventSecondStep(TS ts, PetscReal dt2) 202d71ae5a4SJacob Faibussowitsch { 203458122a4SShri Abhyankar PetscFunctionBegin; 204*fe4ad979SIlya Fursov ts->event->timestep_2nd_postevent = dt2; 2053ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 206458122a4SShri Abhyankar } 207458122a4SShri Abhyankar 208458122a4SShri Abhyankar /*@ 209ca4445c7SIlya Fursov TSSetEventTolerances - Set tolerances for event (indicator function) zero crossings 210e3005195SShri Abhyankar 211e3005195SShri Abhyankar Logically Collective 212e3005195SShri Abhyankar 2134165533cSJose E. Roman Input Parameters: 214e3005195SShri Abhyankar + ts - time integration context 215ca4445c7SIlya Fursov . tol - tolerance, `PETSC_DECIDE` or `PETSC_DEFAULT` to leave the current value 2169b3d3759SBarry Smith - vtol - array of tolerances or `NULL`, used in preference to `tol` if present 217e3005195SShri Abhyankar 218bcf0153eSBarry Smith Options Database Key: 219ca4445c7SIlya Fursov . -ts_event_tol <tol> - tolerance for event (indicator function) zero crossing 220e3005195SShri Abhyankar 221e3005195SShri Abhyankar Level: beginner 222e3005195SShri Abhyankar 223bcf0153eSBarry Smith Notes: 224ca4445c7SIlya Fursov One must call `TSSetEventHandler()` before setting the tolerances. 225bcf0153eSBarry Smith 226ca4445c7SIlya Fursov The size of `vtol` should be equal to the number of events on the given process. 22720f4b53cSBarry Smith 228ca4445c7SIlya Fursov This function can be also called from the `postevent()` callback set with `TSSetEventHandler()`, 229ca4445c7SIlya Fursov to adjust the tolerances on the fly. 230bcf0153eSBarry Smith 2311cc06b55SBarry Smith .seealso: [](ch_ts), `TS`, `TSEvent`, `TSSetEventHandler()` 232e3005195SShri Abhyankar @*/ 233d71ae5a4SJacob Faibussowitsch PetscErrorCode TSSetEventTolerances(TS ts, PetscReal tol, PetscReal vtol[]) 234d71ae5a4SJacob Faibussowitsch { 2356427ac75SLisandro Dalcin TSEvent event; 236e3005195SShri Abhyankar 237e3005195SShri Abhyankar PetscFunctionBegin; 2386427ac75SLisandro Dalcin PetscValidHeaderSpecific(ts, TS_CLASSID, 1); 2394f572ea9SToby Isaac if (vtol) PetscAssertPointer(vtol, 3); 2403c633725SBarry Smith PetscCheck(ts->event, PetscObjectComm((PetscObject)ts), PETSC_ERR_USER, "Must set the events first by calling TSSetEventHandler()"); 2416427ac75SLisandro Dalcin 2426427ac75SLisandro Dalcin event = ts->event; 243e3005195SShri Abhyankar if (vtol) { 244ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->nevents; i++) event->vtol[i] = vtol[i]; 245e3005195SShri Abhyankar } else { 246ca4445c7SIlya Fursov if (tol != (PetscReal)PETSC_DECIDE && tol != (PetscReal)PETSC_DEFAULT) { 247ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->nevents; i++) event->vtol[i] = tol; 248e3005195SShri Abhyankar } 249e3005195SShri Abhyankar } 2503ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 251e3005195SShri Abhyankar } 252e3005195SShri Abhyankar 2532dc7a7e3SShri Abhyankar /*@C 254ca4445c7SIlya Fursov TSSetEventHandler - Sets functions and parameters used for indicating events and handling them 2552dc7a7e3SShri Abhyankar 256c3339decSBarry Smith Logically Collective 2572dc7a7e3SShri Abhyankar 2582dc7a7e3SShri Abhyankar Input Parameters: 259bcf0153eSBarry Smith + ts - the `TS` context obtained from `TSCreate()` 260ca4445c7SIlya Fursov . nevents - number of local events (i.e. managed by the given MPI process) 261ca4445c7SIlya Fursov . direction - direction of zero crossing to be detected (one for each local event). 262ca4445c7SIlya Fursov `-1` => zero crossing in negative direction, 263ca4445c7SIlya Fursov `+1` => zero crossing in positive direction, `0` => both ways 264ca4445c7SIlya Fursov . terminate - flag to indicate whether time stepping should be terminated after 265ca4445c7SIlya Fursov an event is detected (one for each local event) 266ca4445c7SIlya Fursov . indicator - callback defininig the user indicator functions whose sign changes (see `direction`) mark presence of the events 267ca4445c7SIlya Fursov . postevent - [optional] user post-event callback; it can change the solution, ODE etc at the time of the event 268ca4445c7SIlya Fursov - ctx - [optional] user-defined context for private data for the 269ca4445c7SIlya Fursov `indicator()` and `postevent()` routines (use `NULL` if no 270ca4445c7SIlya Fursov context is desired) 2712dc7a7e3SShri Abhyankar 2729b3d3759SBarry Smith Calling sequence of `indicator`: 2732fe279fdSBarry Smith + ts - the `TS` context 2742dc7a7e3SShri Abhyankar . t - current time 275ca4445c7SIlya Fursov . U - current solution 276ca4445c7SIlya Fursov . fvalue - output array with values of local indicator functions (length == `nevents`) for time t and state-vector U 2779b3d3759SBarry Smith - ctx - the context passed as the final argument to `TSSetEventHandler()` 2782dc7a7e3SShri Abhyankar 27920f4b53cSBarry Smith Calling sequence of `postevent`: 2802fe279fdSBarry Smith + ts - the `TS` context 281ca4445c7SIlya Fursov . nevents_zero - number of triggered local events (whose indicator function is marked as crossing zero, and direction is appropriate) 282ca4445c7SIlya Fursov . events_zero - indices of the triggered local events 2832dc7a7e3SShri Abhyankar . t - current time 2842dc7a7e3SShri Abhyankar . U - current solution 285ca4445c7SIlya Fursov . forwardsolve - flag to indicate whether `TS` is doing a forward solve (`PETSC_TRUE`) or adjoint solve (`PETSC_FALSE`) 2869b3d3759SBarry Smith - ctx - the context passed as the final argument to `TSSetEventHandler()` 2872dc7a7e3SShri Abhyankar 288ca4445c7SIlya Fursov Options Database Keys: 289ca4445c7SIlya Fursov + -ts_event_tol <tol> - tolerance for zero crossing check of indicator functions 290ca4445c7SIlya Fursov . -ts_event_monitor - print choices made by event handler 291ca4445c7SIlya Fursov . -ts_event_recorder_initial_size <recsize> - initial size of event recorder 292*fe4ad979SIlya Fursov . -ts_event_post_event_step <dt1> - first time step after event 293*fe4ad979SIlya Fursov . -ts_event_post_event_second_step <dt2> - second time step after event 294ca4445c7SIlya Fursov - -ts_event_dt_min <dt> - minimum time step considered for TSEvent 295ca4445c7SIlya Fursov 2962dc7a7e3SShri Abhyankar Level: intermediate 2972dc7a7e3SShri Abhyankar 298ca4445c7SIlya Fursov Notes: 299ca4445c7SIlya Fursov The indicator functions should be defined in the `indicator` callback using the components of solution `U` and/or time `t`. 300ca4445c7SIlya Fursov Note that `U` is `PetscScalar`-valued, and the indicator functions are `PetscReal`-valued. It is the user's responsibility to 301ca4445c7SIlya Fursov properly handle this difference, e.g. by applying `PetscRealPart()` or other appropriate conversion means. 302ca4445c7SIlya Fursov 303ca4445c7SIlya Fursov The full set of events is distributed (by the user design) across MPI processes, with each process defining its own local sub-set of events. 304ca4445c7SIlya Fursov However, the `postevent()` callback invocation is performed synchronously on all processes, including 305ca4445c7SIlya Fursov those processes which have not currently triggered any events. 306ca4445c7SIlya Fursov 3071cc06b55SBarry Smith .seealso: [](ch_ts), `TSEvent`, `TSCreate()`, `TSSetTimeStep()`, `TSSetConvergedReason()` 3082dc7a7e3SShri Abhyankar @*/ 309ca4445c7SIlya Fursov PetscErrorCode TSSetEventHandler(TS ts, PetscInt nevents, PetscInt direction[], PetscBool terminate[], PetscErrorCode (*indicator)(TS ts, PetscReal t, Vec U, PetscReal fvalue[], void *ctx), PetscErrorCode (*postevent)(TS ts, PetscInt nevents_zero, PetscInt events_zero[], PetscReal t, Vec U, PetscBool forwardsolve, void *ctx), void *ctx) 310d71ae5a4SJacob Faibussowitsch { 311d294eb03SHong Zhang TSAdapt adapt; 312d294eb03SHong Zhang PetscReal hmin; 3132dc7a7e3SShri Abhyankar TSEvent event; 314006e6a18SShri Abhyankar PetscBool flg; 315a6c783d2SShri Abhyankar #if defined PETSC_USE_REAL_SINGLE 316a6c783d2SShri Abhyankar PetscReal tol = 1e-4; 317a6c783d2SShri Abhyankar #else 318d569cc17SSatish Balay PetscReal tol = 1e-6; 319a6c783d2SShri Abhyankar #endif 3202dc7a7e3SShri Abhyankar 3212dc7a7e3SShri Abhyankar PetscFunctionBegin; 3226427ac75SLisandro Dalcin PetscValidHeaderSpecific(ts, TS_CLASSID, 1); 3230a82b154SShri if (nevents) { 3244f572ea9SToby Isaac PetscAssertPointer(direction, 3); 3254f572ea9SToby Isaac PetscAssertPointer(terminate, 4); 3260a82b154SShri } 3274dfa11a4SJacob Faibussowitsch PetscCall(PetscNew(&event)); 3289566063dSJacob Faibussowitsch PetscCall(PetscMalloc1(nevents, &event->fvalue_prev)); 329ca4445c7SIlya Fursov PetscCall(PetscMalloc1(nevents, &event->fvalue)); 3309566063dSJacob Faibussowitsch PetscCall(PetscMalloc1(nevents, &event->fvalue_right)); 331ca4445c7SIlya Fursov PetscCall(PetscMalloc1(nevents, &event->fsign_prev)); 332ca4445c7SIlya Fursov PetscCall(PetscMalloc1(nevents, &event->fsign)); 333ca4445c7SIlya Fursov PetscCall(PetscMalloc1(nevents, &event->fsign_right)); 3349566063dSJacob Faibussowitsch PetscCall(PetscMalloc1(nevents, &event->side)); 335ca4445c7SIlya Fursov PetscCall(PetscMalloc1(nevents, &event->side_prev)); 336ca4445c7SIlya Fursov PetscCall(PetscMalloc1(nevents, &event->justrefined_AB)); 337ca4445c7SIlya Fursov PetscCall(PetscMalloc1(nevents, &event->gamma_AB)); 3389566063dSJacob Faibussowitsch PetscCall(PetscMalloc1(nevents, &event->direction)); 3399566063dSJacob Faibussowitsch PetscCall(PetscMalloc1(nevents, &event->terminate)); 340ca4445c7SIlya Fursov PetscCall(PetscMalloc1(nevents, &event->events_zero)); 3419566063dSJacob Faibussowitsch PetscCall(PetscMalloc1(nevents, &event->vtol)); 342ca4445c7SIlya Fursov for (PetscInt i = 0; i < nevents; i++) { 343d94325d3SShri Abhyankar event->direction[i] = direction[i]; 344d94325d3SShri Abhyankar event->terminate[i] = terminate[i]; 345ca4445c7SIlya Fursov event->justrefined_AB[i] = PETSC_FALSE; 346ca4445c7SIlya Fursov event->gamma_AB[i] = 1; 347ca4445c7SIlya Fursov event->side[i] = 2; 348ca4445c7SIlya Fursov event->side_prev[i] = 0; 349d94325d3SShri Abhyankar } 350ca4445c7SIlya Fursov event->iterctr = 0; 351ca4445c7SIlya Fursov event->processing = PETSC_FALSE; 352ca4445c7SIlya Fursov event->revisit_right = PETSC_FALSE; 3532589fa24SLisandro Dalcin event->nevents = nevents; 3549b3d3759SBarry Smith event->indicator = indicator; 3552dc7a7e3SShri Abhyankar event->postevent = postevent; 3566427ac75SLisandro Dalcin event->ctx = ctx; 357*fe4ad979SIlya Fursov event->timestep_postevent = PETSC_DECIDE; 358*fe4ad979SIlya Fursov event->timestep_2nd_postevent = PETSC_DECIDE; 3599566063dSJacob Faibussowitsch PetscCall(TSGetAdapt(ts, &adapt)); 3609566063dSJacob Faibussowitsch PetscCall(TSAdaptGetStepLimits(adapt, &hmin, NULL)); 361d294eb03SHong Zhang event->timestep_min = hmin; 3622dc7a7e3SShri Abhyankar 36302749585SLisandro Dalcin event->recsize = 8; /* Initial size of the recorder */ 364d0609cedSBarry Smith PetscOptionsBegin(((PetscObject)ts)->comm, ((PetscObject)ts)->prefix, "TS Event options", "TS"); 3652dc7a7e3SShri Abhyankar { 366ca4445c7SIlya Fursov PetscCall(PetscOptionsReal("-ts_event_tol", "Tolerance for zero crossing check of indicator functions", "TSSetEventTolerances", tol, &tol, NULL)); 3679566063dSJacob Faibussowitsch PetscCall(PetscOptionsName("-ts_event_monitor", "Print choices made by event handler", "", &flg)); 3689566063dSJacob Faibussowitsch PetscCall(PetscOptionsInt("-ts_event_recorder_initial_size", "Initial size of event recorder", "", event->recsize, &event->recsize, NULL)); 369*fe4ad979SIlya Fursov PetscCall(PetscOptionsDeprecated("-ts_event_post_eventinterval_step", "-ts_event_post_event_second_step", "3.21", NULL)); 370*fe4ad979SIlya Fursov PetscCall(PetscOptionsReal("-ts_event_post_event_step", "First time step after event", "", event->timestep_postevent, &event->timestep_postevent, NULL)); 371*fe4ad979SIlya Fursov PetscCall(PetscOptionsReal("-ts_event_post_event_second_step", "Second time step after event", "", event->timestep_2nd_postevent, &event->timestep_2nd_postevent, NULL)); 3729566063dSJacob Faibussowitsch PetscCall(PetscOptionsReal("-ts_event_dt_min", "Minimum time step considered for TSEvent", "", event->timestep_min, &event->timestep_min, NULL)); 3732dc7a7e3SShri Abhyankar } 374d0609cedSBarry Smith PetscOptionsEnd(); 375a4ffd976SShri Abhyankar 3769566063dSJacob Faibussowitsch PetscCall(PetscMalloc1(event->recsize, &event->recorder.time)); 3779566063dSJacob Faibussowitsch PetscCall(PetscMalloc1(event->recsize, &event->recorder.stepnum)); 3789566063dSJacob Faibussowitsch PetscCall(PetscMalloc1(event->recsize, &event->recorder.nevents)); 3799566063dSJacob Faibussowitsch PetscCall(PetscMalloc1(event->recsize, &event->recorder.eventidx)); 380ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->recsize; i++) PetscCall(PetscMalloc1(event->nevents, &event->recorder.eventidx[i])); 381e7069c78SShri /* Initialize the event recorder */ 382e7069c78SShri event->recorder.ctr = 0; 383a4ffd976SShri Abhyankar 384ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->nevents; i++) event->vtol[i] = tol; 3859566063dSJacob Faibussowitsch if (flg) PetscCall(PetscViewerASCIIOpen(PETSC_COMM_SELF, "stdout", &event->monitor)); 3869e12be75SShri Abhyankar 3879566063dSJacob Faibussowitsch PetscCall(TSEventDestroy(&ts->event)); 388d94325d3SShri Abhyankar ts->event = event; 389e7069c78SShri ts->event->refct = 1; 3903ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 3912dc7a7e3SShri Abhyankar } 3922dc7a7e3SShri Abhyankar 393a4ffd976SShri Abhyankar /* 394a4ffd976SShri Abhyankar TSEventRecorderResize - Resizes (2X) the event recorder arrays whenever the recording limit (event->recsize) 395a4ffd976SShri Abhyankar is reached. 396a4ffd976SShri Abhyankar */ 397d71ae5a4SJacob Faibussowitsch static PetscErrorCode TSEventRecorderResize(TSEvent event) 398d71ae5a4SJacob Faibussowitsch { 399a4ffd976SShri Abhyankar PetscReal *time; 400ca4445c7SIlya Fursov PetscInt *stepnum, *nevents; 401a4ffd976SShri Abhyankar PetscInt **eventidx; 402ca4445c7SIlya Fursov PetscInt fact = 2; 403a4ffd976SShri Abhyankar 404a4ffd976SShri Abhyankar PetscFunctionBegin; 405ca4445c7SIlya Fursov /* Create larger arrays */ 4069566063dSJacob Faibussowitsch PetscCall(PetscMalloc1(fact * event->recsize, &time)); 4079566063dSJacob Faibussowitsch PetscCall(PetscMalloc1(fact * event->recsize, &stepnum)); 4089566063dSJacob Faibussowitsch PetscCall(PetscMalloc1(fact * event->recsize, &nevents)); 4099566063dSJacob Faibussowitsch PetscCall(PetscMalloc1(fact * event->recsize, &eventidx)); 410ca4445c7SIlya Fursov for (PetscInt i = 0; i < fact * event->recsize; i++) PetscCall(PetscMalloc1(event->nevents, &eventidx[i])); 411a4ffd976SShri Abhyankar 412a4ffd976SShri Abhyankar /* Copy over data */ 4139566063dSJacob Faibussowitsch PetscCall(PetscArraycpy(time, event->recorder.time, event->recsize)); 4149566063dSJacob Faibussowitsch PetscCall(PetscArraycpy(stepnum, event->recorder.stepnum, event->recsize)); 4159566063dSJacob Faibussowitsch PetscCall(PetscArraycpy(nevents, event->recorder.nevents, event->recsize)); 416ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->recsize; i++) PetscCall(PetscArraycpy(eventidx[i], event->recorder.eventidx[i], event->recorder.nevents[i])); 417a4ffd976SShri Abhyankar 418a4ffd976SShri Abhyankar /* Destroy old arrays */ 419ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->recsize; i++) PetscCall(PetscFree(event->recorder.eventidx[i])); 4209566063dSJacob Faibussowitsch PetscCall(PetscFree(event->recorder.eventidx)); 4219566063dSJacob Faibussowitsch PetscCall(PetscFree(event->recorder.nevents)); 4229566063dSJacob Faibussowitsch PetscCall(PetscFree(event->recorder.stepnum)); 4239566063dSJacob Faibussowitsch PetscCall(PetscFree(event->recorder.time)); 424a4ffd976SShri Abhyankar 425a4ffd976SShri Abhyankar /* Set pointers */ 426a4ffd976SShri Abhyankar event->recorder.time = time; 427a4ffd976SShri Abhyankar event->recorder.stepnum = stepnum; 428a4ffd976SShri Abhyankar event->recorder.nevents = nevents; 429a4ffd976SShri Abhyankar event->recorder.eventidx = eventidx; 430a4ffd976SShri Abhyankar 431ca4445c7SIlya Fursov /* Update the size */ 432a4ffd976SShri Abhyankar event->recsize *= fact; 4333ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 434a4ffd976SShri Abhyankar } 435a4ffd976SShri Abhyankar 436031fbad4SShri Abhyankar /* 437ca4445c7SIlya Fursov Helper routine to handle user postevents and recording 438031fbad4SShri Abhyankar */ 439d71ae5a4SJacob Faibussowitsch static PetscErrorCode TSPostEvent(TS ts, PetscReal t, Vec U) 440d71ae5a4SJacob Faibussowitsch { 4412dc7a7e3SShri Abhyankar TSEvent event = ts->event; 44228d5b5d6SLisandro Dalcin PetscBool restart = PETSC_FALSE; 443ca4445c7SIlya Fursov PetscBool terminate = PETSC_FALSE; 444ca4445c7SIlya Fursov PetscBool statechanged = PETSC_FALSE; 445ca4445c7SIlya Fursov PetscInt ctr, stepnum; 446ca4445c7SIlya Fursov PetscBool inflag[3], outflag[3]; 447ca4445c7SIlya Fursov PetscBool forwardsolve = PETSC_TRUE; // Flag indicating that TS is doing a forward solve 4482dc7a7e3SShri Abhyankar 4492dc7a7e3SShri Abhyankar PetscFunctionBegin; 4502dc7a7e3SShri Abhyankar if (event->postevent) { 45128d5b5d6SLisandro Dalcin PetscObjectState state_prev, state_post; 4529566063dSJacob Faibussowitsch PetscCall(PetscObjectStateGet((PetscObject)U, &state_prev)); 453ca4445c7SIlya Fursov PetscCallBack("TSEvent post-event processing", (*event->postevent)(ts, event->nevents_zero, event->events_zero, t, U, forwardsolve, event->ctx)); // TODO update 'restart' here? 4549566063dSJacob Faibussowitsch PetscCall(PetscObjectStateGet((PetscObject)U, &state_post)); 455ca4445c7SIlya Fursov if (state_prev != state_post) { 456ca4445c7SIlya Fursov restart = PETSC_TRUE; 457ca4445c7SIlya Fursov statechanged = PETSC_TRUE; 458ca4445c7SIlya Fursov } 4592dc7a7e3SShri Abhyankar } 4604597913aSLisandro Dalcin 461ca4445c7SIlya Fursov // Handle termination events and step restart 462ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->nevents_zero; i++) 4639371c9d4SSatish Balay if (event->terminate[event->events_zero[i]]) terminate = PETSC_TRUE; 4649371c9d4SSatish Balay inflag[0] = restart; 4659371c9d4SSatish Balay inflag[1] = terminate; 466ca4445c7SIlya Fursov inflag[2] = statechanged; 467ca4445c7SIlya Fursov PetscCall(MPIU_Allreduce(inflag, outflag, 3, MPIU_BOOL, MPI_LOR, ((PetscObject)ts)->comm)); 4689371c9d4SSatish Balay restart = outflag[0]; 4699371c9d4SSatish Balay terminate = outflag[1]; 470ca4445c7SIlya Fursov statechanged = outflag[2]; 4719566063dSJacob Faibussowitsch if (restart) PetscCall(TSRestartStep(ts)); 4729566063dSJacob Faibussowitsch if (terminate) PetscCall(TSSetConvergedReason(ts, TS_CONVERGED_EVENT)); 473f7aea88cSShri Abhyankar 474ca4445c7SIlya Fursov /* 475ca4445c7SIlya Fursov Recalculate the indicator functions and signs if the state has been changed by the user postevent callback. 476ca4445c7SIlya Fursov Note! If the state HAS NOT changed, the existing event->fsign (equal to zero) is kept, which: 477ca4445c7SIlya Fursov - might have been defined using the previous (now-possibly-overridden) event->vtol, 478ca4445c7SIlya Fursov - might have been set to zero on reaching a small time step rather than using the vtol criterion. 479ca4445c7SIlya Fursov This will enforce keeping event->fsign = 0 where the zero-crossings were actually marked, 480ca4445c7SIlya Fursov resulting in a more consistent behaviour of fsign's. 481ca4445c7SIlya Fursov */ 482ca4445c7SIlya Fursov if (statechanged) { 483ca4445c7SIlya Fursov if (event->monitor) PetscCall(PetscPrintf(((PetscObject)ts)->comm, "TSEvent: at time %g the vector state has been changed by PostEvent, recalculating fvalues and signs\n", (double)t)); 4849566063dSJacob Faibussowitsch PetscCall(VecLockReadPush(U)); 485ca4445c7SIlya Fursov PetscCallBack("TSEvent indicator", (*event->indicator)(ts, t, U, event->fvalue, event->ctx)); 4869566063dSJacob Faibussowitsch PetscCall(VecLockReadPop(U)); 487ca4445c7SIlya Fursov TSEventCalcSigns(event->nevents, event->fvalue, event->vtol, event->fsign); // note, event->vtol might have been changed by the postevent() 488f443add6SLisandro Dalcin } 489f443add6SLisandro Dalcin 490ca4445c7SIlya Fursov // Record the event in the event recorder 4919566063dSJacob Faibussowitsch PetscCall(TSGetStepNumber(ts, &stepnum)); 492f7aea88cSShri Abhyankar ctr = event->recorder.ctr; 49348a46eb9SPierre Jolivet if (ctr == event->recsize) PetscCall(TSEventRecorderResize(event)); 494f7aea88cSShri Abhyankar event->recorder.time[ctr] = t; 495d0578d90SShri Abhyankar event->recorder.stepnum[ctr] = stepnum; 4964597913aSLisandro Dalcin event->recorder.nevents[ctr] = event->nevents_zero; 497ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->nevents_zero; i++) event->recorder.eventidx[ctr][i] = event->events_zero[i]; 498f7aea88cSShri Abhyankar event->recorder.ctr++; 4993ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 5002dc7a7e3SShri Abhyankar } 5012dc7a7e3SShri Abhyankar 502ca4445c7SIlya Fursov // PetscClangLinter pragma disable: -fdoc-sowing-chars 503ca4445c7SIlya Fursov /* 504ca4445c7SIlya Fursov (modified) Anderson-Bjorck variant of regula falsi method, refines [tleft, t] or [t, tright] based on 'side' (-1 or +1). 505ca4445c7SIlya Fursov The scaling parameter is defined based on the 'justrefined' flag, the history of repeats of 'side', and the threshold. 506ca4445c7SIlya Fursov To escape certain failure modes, the algorithm may drift towards the bisection rule. 507ca4445c7SIlya Fursov The value pointed to by 'side_prev' gets updated. 508ca4445c7SIlya Fursov This function returns the new time step. 509ca4445c7SIlya Fursov 510ca4445c7SIlya Fursov The underlying pure Anderson-Bjorck algorithm was taken as described in 511ca4445c7SIlya Fursov J.M. Fernandez-Diaz, C.O. Menendez-Perez "A common framework for modified Regula Falsi methods and new methods of this kind", 2023. 512ca4445c7SIlya Fursov The modifications subsequently introduced have little effect on the behaviour for simple cases requiring only a few iterations 513ca4445c7SIlya Fursov (some minor convergence slowdown may take place though), but the effect becomes more pronounced for tough cases requiring many iterations. 514ca4445c7SIlya Fursov For the latter situation the speed-up may be order(s) of magnitude compared to the classical Anderson-Bjorck. 515ca4445c7SIlya Fursov The modifications (the threshold trick, and the drift towards bisection) were tested and tweaked 516ca4445c7SIlya Fursov based on a number of test functions from the mentioned paper. 517ca4445c7SIlya Fursov */ 518ca4445c7SIlya Fursov static inline PetscReal RefineAndersonBjorck(PetscReal tleft, PetscReal t, PetscReal tright, PetscReal fleft, PetscReal f, PetscReal fright, PetscInt side, PetscInt *side_prev, PetscBool justrefined, PetscReal *gamma) 519d71ae5a4SJacob Faibussowitsch { 520ca4445c7SIlya Fursov PetscReal new_dt, scal = 1.0, scalB = 1.0, threshold = 0.0, power; 521ca4445c7SIlya Fursov PetscInt reps = 0; 522ca4445c7SIlya Fursov const PetscInt REPS_CAP = 8; // an upper bound to be imposed on 'reps' (set to 8, somewhat arbitrary number, found after some tweaking) 523ca4445c7SIlya Fursov 524ca4445c7SIlya Fursov // Preparations 525ca4445c7SIlya Fursov if (justrefined) { 526ca4445c7SIlya Fursov if (*side_prev * side > 0) *side_prev += side; // the side keeps repeating -> increment the side counter (-ve or +ve) 527ca4445c7SIlya Fursov else *side_prev = side; // reset the counter 528ca4445c7SIlya Fursov reps = PetscMin(*side_prev * side, REPS_CAP); // the length of the recent side-repeat series, including the current 'side' 529ca4445c7SIlya Fursov threshold = PetscPowReal(0.5, reps) * 0.1; // ad-hoc strategy for threshold calculation (involved some tweaking) 530ca4445c7SIlya Fursov } else *side_prev = side; // initial reset of the counter 531ca4445c7SIlya Fursov 532ca4445c7SIlya Fursov // Time step calculation 533e2cdd850SShri Abhyankar if (side == -1) { 534ca4445c7SIlya Fursov if (justrefined && fright != 0.0 && fleft != 0.0) { 535ca4445c7SIlya Fursov scal = (fright - f) / fright; 536ca4445c7SIlya Fursov scalB = -f / fleft; 537e2cdd850SShri Abhyankar } 538ca4445c7SIlya Fursov } else { // must be side == +1 539ca4445c7SIlya Fursov if (justrefined && fleft != 0.0 && fright != 0.0) { 540ca4445c7SIlya Fursov scal = (fleft - f) / fleft; 541ca4445c7SIlya Fursov scalB = -f / fright; 542e2cdd850SShri Abhyankar } 54338bf2713SShri Abhyankar } 544e2cdd850SShri Abhyankar 545ca4445c7SIlya Fursov if (scal < threshold) scal = 0.5; 546ca4445c7SIlya Fursov if (reps > 1) *gamma *= scal; // side did not switch since the last time, accumulate gamma 547ca4445c7SIlya Fursov else *gamma = 1.0; // side switched -> reset gamma 548ca4445c7SIlya Fursov power = PetscMax(0.0, (reps - 2.0) / (REPS_CAP - 2.0)); 549ca4445c7SIlya Fursov scal = PetscPowReal(scalB / *gamma, power) * (*gamma); // mix the Anderson-Bjorck scaling and Bisection scaling 550ca4445c7SIlya Fursov 551ca4445c7SIlya Fursov if (side == -1) new_dt = scal * fleft / (scal * fleft - f) * (t - tleft); 552ca4445c7SIlya Fursov else new_dt = f / (f - scal * fright) * (tright - t); 553ca4445c7SIlya Fursov /* 554ca4445c7SIlya Fursov In tough cases (e.g. a polynomial of high order), there is a failure mode for the standard Anderson-Bjorck, 555ca4445c7SIlya Fursov when the new proposed point jumps from one end-point of the bracket to the other, however the bracket 556ca4445c7SIlya Fursov is contracting very slowly. A larger threshold for 'scal' prevents entering this mode. 557ca4445c7SIlya Fursov On the other hand, if the iteration gets stuck near one end-point of the bracket, and the 'side' does not switch for a while, 558ca4445c7SIlya Fursov the 'scal' drifts towards the bisection approach (via scalB), ensuring stable convergence. 559ca4445c7SIlya Fursov */ 560ca4445c7SIlya Fursov return new_dt; 561ca4445c7SIlya Fursov } 562ca4445c7SIlya Fursov 563ca4445c7SIlya Fursov /* 564ca4445c7SIlya Fursov Checks if the current point (t) is the zero-crossing location, based on the indicator function signs and direction[]: 565ca4445c7SIlya Fursov - using the dt_min criterion, 566ca4445c7SIlya Fursov - using the vtol criterion. 567ca4445c7SIlya Fursov The situation (fsign_prev, fsign) = (0, 0) is treated as staying in the near-zero-zone of the previous zero-crossing, 568ca4445c7SIlya Fursov and is not marked as a new zero-crossing. 569ca4445c7SIlya Fursov This function may update event->side[]. 570ca4445c7SIlya Fursov */ 571ca4445c7SIlya Fursov static PetscErrorCode TSEventTestZero(TS ts, PetscReal t) 572d71ae5a4SJacob Faibussowitsch { 573d294eb03SHong Zhang TSEvent event = ts->event; 574d294eb03SHong Zhang 575d294eb03SHong Zhang PetscFunctionBegin; 576ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->nevents; i++) { 577ca4445c7SIlya Fursov const PetscBool bracket_is_left = (event->fsign_prev[i] * event->fsign[i] < 0 && event->fsign[i] * event->direction[i] >= 0) ? PETSC_TRUE : PETSC_FALSE; 578d294eb03SHong Zhang 579ca4445c7SIlya Fursov if (bracket_is_left && ((t - event->ptime_prev <= event->timestep_min) || event->revisit_right)) event->side[i] = 0; // mark zero-crossing from dt_min; 'bracket_is_left' accounts for direction 580ca4445c7SIlya Fursov if (event->fsign[i] == 0 && event->fsign_prev[i] != 0 && event->fsign_prev[i] * event->direction[i] <= 0) event->side[i] = 0; // mark zero-crossing from vtol 581d294eb03SHong Zhang } 5823ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 583d294eb03SHong Zhang } 58438bf2713SShri Abhyankar 585ca4445c7SIlya Fursov /* 586ca4445c7SIlya Fursov Checks if [fleft, f] or [f, fright] are 'brackets', i.e. intervals with the sign change, satisfying the 'direction'. 587ca4445c7SIlya Fursov The right interval is only checked if iterctr > 0 (i.e. Anderson-Bjorck refinement has started). 588ca4445c7SIlya Fursov The intervals like [0, x] and [x, 0] are not counted as brackets, i.e. intervals with the sign change. 589ca4445c7SIlya Fursov The function returns the 'side' value: -1 (left, or both are brackets), +1 (only right one), +2 (neither). 590ca4445c7SIlya Fursov */ 591ca4445c7SIlya Fursov static inline PetscInt TSEventTestBracket(PetscInt fsign_left, PetscInt fsign, PetscInt fsign_right, PetscInt direction, PetscInt iterctr) 592ca4445c7SIlya Fursov { 593ca4445c7SIlya Fursov PetscInt side = 2; 594ca4445c7SIlya Fursov if (fsign_left * fsign < 0 && fsign * direction >= 0) side = -1; 595ca4445c7SIlya Fursov if (side != -1 && iterctr > 0 && fsign * fsign_right < 0 && fsign_right * direction >= 0) side = 1; 596ca4445c7SIlya Fursov return side; 597ca4445c7SIlya Fursov } 598ca4445c7SIlya Fursov 599ca4445c7SIlya Fursov /* 600ca4445c7SIlya Fursov Caps the time steps, accounting for time span points. 601ca4445c7SIlya Fursov It uses 'event->timestep_cache' as a time step to calculate the tolerance for tspan points detection. This 602ca4445c7SIlya Fursov is done since the event resolution may result in significant time step refinement, and we don't use these small steps for tolerances. 603ca4445c7SIlya Fursov To enhance the consistency of tspan points detection, tolerance 'tspan->worktol' is reused later in the TSSolve iteration. 604ca4445c7SIlya Fursov If a user-defined step is cut by this function, the input uncut step is saved to adapt->dt_span_cached. 605ca4445c7SIlya Fursov Flag 'user_dt' indicates if the step was defined by user. 606ca4445c7SIlya Fursov */ 607ca4445c7SIlya Fursov static inline PetscReal TSEvent_dt_cap(TS ts, PetscReal t, PetscReal dt, PetscBool user_dt) 608ca4445c7SIlya Fursov { 609ca4445c7SIlya Fursov PetscReal res = dt; 610ca4445c7SIlya Fursov if (ts->exact_final_time == TS_EXACTFINALTIME_MATCHSTEP) { 611ca4445c7SIlya Fursov PetscReal maxdt = ts->max_time - t; // this may be overriden by tspan 612ca4445c7SIlya Fursov PetscBool cut_made = PETSC_FALSE; 613ca4445c7SIlya Fursov PetscReal eps = 10 * PETSC_MACHINE_EPSILON; 614ca4445c7SIlya Fursov if (ts->tspan) { 615ca4445c7SIlya Fursov PetscInt ctr = ts->tspan->spanctr; 616ca4445c7SIlya Fursov PetscInt Ns = ts->tspan->num_span_times; 617ca4445c7SIlya Fursov PetscReal *st = ts->tspan->span_times; 618ca4445c7SIlya Fursov 619ca4445c7SIlya Fursov if (ts->tspan->worktol == 0) ts->tspan->worktol = ts->tspan->reltol * ts->event->timestep_cache + ts->tspan->abstol; // in case TSAdaptChoose() has not defined it 620ca4445c7SIlya Fursov if (ctr < Ns && PetscIsCloseAtTol(t, st[ctr], ts->tspan->worktol, 0)) { // just hit a time span point 621ca4445c7SIlya Fursov if (ctr + 1 < Ns) maxdt = st[ctr + 1] - t; // ok to use the next time span point 622ca4445c7SIlya Fursov else maxdt = ts->max_time - t; // can't use the next time span point: they have finished 623ca4445c7SIlya Fursov } else if (ctr < Ns) maxdt = st[ctr] - t; // haven't hit a time span point, use the nearest one 624ca4445c7SIlya Fursov } 625ca4445c7SIlya Fursov maxdt = PetscMin(maxdt, ts->max_time - t); 626ca4445c7SIlya Fursov PetscCheck((maxdt > eps) || (PetscAbsReal(maxdt) <= eps && PetscIsCloseAtTol(t, ts->max_time, eps, 0)), PetscObjectComm((PetscObject)ts), PETSC_ERR_PLIB, "Unexpected state: bad maxdt in TSEvent_dt_cap()"); 627ca4445c7SIlya Fursov 628ca4445c7SIlya Fursov if (PetscIsCloseAtTol(dt, maxdt, eps, 0)) res = maxdt; // no cut 629ca4445c7SIlya Fursov else { 630ca4445c7SIlya Fursov if (dt > maxdt) { 631ca4445c7SIlya Fursov res = maxdt; // yes cut 632ca4445c7SIlya Fursov cut_made = PETSC_TRUE; 633ca4445c7SIlya Fursov } else res = dt; // no cut 634ca4445c7SIlya Fursov } 635ca4445c7SIlya Fursov if (ts->adapt && user_dt) { // only update dt_span_cached for the user-defined step 636ca4445c7SIlya Fursov if (cut_made) ts->adapt->dt_span_cached = dt; 637ca4445c7SIlya Fursov else ts->adapt->dt_span_cached = 0; 638ca4445c7SIlya Fursov } 639ca4445c7SIlya Fursov } 640ca4445c7SIlya Fursov return res; 641ca4445c7SIlya Fursov } 642ca4445c7SIlya Fursov 643ca4445c7SIlya Fursov /* 644ca4445c7SIlya Fursov Updates the left-end values 645ca4445c7SIlya Fursov */ 646ca4445c7SIlya Fursov static inline void TSEvent_update_left(TSEvent event, PetscReal t) 647ca4445c7SIlya Fursov { 648ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->nevents; i++) { 649ca4445c7SIlya Fursov event->fvalue_prev[i] = event->fvalue[i]; 650ca4445c7SIlya Fursov event->fsign_prev[i] = event->fsign[i]; 651ca4445c7SIlya Fursov } 652ca4445c7SIlya Fursov event->ptime_prev = t; 653ca4445c7SIlya Fursov } 654ca4445c7SIlya Fursov 655ca4445c7SIlya Fursov /* 656ca4445c7SIlya Fursov Updates the right-end values 657ca4445c7SIlya Fursov */ 658ca4445c7SIlya Fursov static inline void TSEvent_update_right(TSEvent event, PetscReal t) 659ca4445c7SIlya Fursov { 660ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->nevents; i++) { 661ca4445c7SIlya Fursov event->fvalue_right[i] = event->fvalue[i]; 662ca4445c7SIlya Fursov event->fsign_right[i] = event->fsign[i]; 663ca4445c7SIlya Fursov } 664ca4445c7SIlya Fursov event->ptime_right = t; 665ca4445c7SIlya Fursov } 666ca4445c7SIlya Fursov 667ca4445c7SIlya Fursov /* 668ca4445c7SIlya Fursov Updates the current values from the right-end values 669ca4445c7SIlya Fursov */ 670ca4445c7SIlya Fursov static inline PetscReal TSEvent_update_from_right(TSEvent event) 671ca4445c7SIlya Fursov { 672ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->nevents; i++) { 673ca4445c7SIlya Fursov event->fvalue[i] = event->fvalue_right[i]; 674ca4445c7SIlya Fursov event->fsign[i] = event->fsign_right[i]; 675ca4445c7SIlya Fursov } 676ca4445c7SIlya Fursov return event->ptime_right; 677ca4445c7SIlya Fursov } 678ca4445c7SIlya Fursov 679*fe4ad979SIlya Fursov static inline PetscBool Not_PETSC_DECIDE(PetscReal dt) 680*fe4ad979SIlya Fursov { 681*fe4ad979SIlya Fursov return (dt == PETSC_DECIDE ? PETSC_FALSE : PETSC_TRUE); 682*fe4ad979SIlya Fursov } 683*fe4ad979SIlya Fursov 684ca4445c7SIlya Fursov // PetscClangLinter pragma disable: -fdoc-section-spacing 685ca4445c7SIlya Fursov // PetscClangLinter pragma disable: -fdoc-section-header-unknown 686ca4445c7SIlya Fursov // PetscClangLinter pragma disable: -fdoc-section-header-spelling 687ca4445c7SIlya Fursov // PetscClangLinter pragma disable: -fdoc-section-header-missing 688ca4445c7SIlya Fursov // PetscClangLinter pragma disable: -fdoc-section-header-fishy-header 689ca4445c7SIlya Fursov // PetscClangLinter pragma disable: -fdoc-param-list-func-parameter-documentation 690ca4445c7SIlya Fursov // PetscClangLinter pragma disable: -fdoc-synopsis-missing-description 691ca4445c7SIlya Fursov // PetscClangLinter pragma disable: -fdoc-sowing-chars 692ca4445c7SIlya Fursov /* 693ca4445c7SIlya Fursov TSEventHandler - the main function to perform a single iteration of event detection. 694ca4445c7SIlya Fursov 695ca4445c7SIlya Fursov Developer notes: 696*fe4ad979SIlya Fursov A) The 'event->iterctr > 0' is used as an indicator that Anderson-Bjorck refinement has started. 697*fe4ad979SIlya Fursov B) If event->iterctr == 0, then justrefined_AB[i] is always false. 698*fe4ad979SIlya Fursov C) The right-end quantities: ptime_right, fvalue_right[i] and fsign_right[i] are only guaranteed to be valid 699ca4445c7SIlya Fursov for event->iterctr > 0. 700*fe4ad979SIlya Fursov D) If event->iterctr > 0, then event->processing is PETSC_TRUE; the opposite may not hold. 701*fe4ad979SIlya Fursov E) When event->processing == PETSC_TRUE and event->iterctr == 0, the event handler iterations are complete, but 702*fe4ad979SIlya Fursov the event handler continues managing the 1st and 2nd post-event steps. In this case the 1st post-event step 703*fe4ad979SIlya Fursov proposed by the event handler is not checked by TSAdapt, and is always accepted (beware!). 704*fe4ad979SIlya Fursov However, if the 2nd post-event step is not managed by the event handler (e.g. 1st = numerical, 2nd = PETSC_DECIDE), 705*fe4ad979SIlya Fursov condition "E" does not hold, and TSAdapt may reject/adjust the 1st post-event step. 706*fe4ad979SIlya Fursov F) event->side[i] may take values: 0 <=> point t is a zero-crossing for indicator function i (via vtol/dt_min criterion); 707ca4445c7SIlya Fursov -1/+1 <=> detected a bracket to the left/right of t for indicator function i; +2 <=> no brackets/zero-crossings. 708*fe4ad979SIlya Fursov G) The signs event->fsign[i] (with values 0/-1/+1) are calculated for each new point. Zero sign is set if the function value is 709ca4445c7SIlya Fursov smaller than the tolerance. Besides, zero sign is enforced after marking a zero-crossing due to small bracket size criterion. 710ca4445c7SIlya Fursov 711ca4445c7SIlya Fursov The intervals with the indicator function sign change (i.e. containing the potential zero-crossings) are called 'brackets'. 712ca4445c7SIlya Fursov To find a zero-crossing, the algorithm first locates a bracket, and then sequentially subdivides it, generating a sequence 713ca4445c7SIlya Fursov of brackets whose length tends to zero. The bracket subdivision involves the (modified) Anderson-Bjorck method. 714ca4445c7SIlya Fursov 715ca4445c7SIlya Fursov Apart from the comments scattered throughout the code to clarify different lines and blocks, 716ca4445c7SIlya Fursov a few tricky aspects of the algorithm (and the underlying reasoning) are discussed in detail below: 717ca4445c7SIlya Fursov 718ca4445c7SIlya Fursov =Sign tracking= 719ca4445c7SIlya Fursov When a zero-crossing is found, the sign variable (event->fsign[i]) is set to zero for the current point t. 720ca4445c7SIlya Fursov This happens both for zero-crossings triggered via the vtol criterion, and those triggered via the dt_min 721ca4445c7SIlya Fursov criterion. After the event, as the TS steps forward, the current sign values are handed over to event->fsign_prev[i]. 722ca4445c7SIlya Fursov The recalculation of signs is avoided if possible: e.g. if a 'vtol' criterion resulted in a zero-crossing at point t, 723ca4445c7SIlya Fursov but the subsequent call to postevent() handler decreased 'vtol', making the indicator function no longer "close to zero" 724ca4445c7SIlya Fursov at point t, the fsign[i] will still consistently keep the zero value. This allows avoiding the erroneous duplication 725ca4445c7SIlya Fursov of events: 726ca4445c7SIlya Fursov 727ca4445c7SIlya Fursov E.g. consider a bracket [t0, t2], where f0 < 0, f2 > 0, which resulted in a zero-crossing t1 with f1 < 0, abs(f1) < vtol. 728ca4445c7SIlya Fursov Suppose the postevent() handler changes vtol to vtol*, such that abs(f1) > vtol*. The TS makes a step t1 -> t3, where 729ca4445c7SIlya Fursov again f1 < 0, f3 > 0, and the event handler will find a new event near t1, which is actually a duplication of the 730ca4445c7SIlya Fursov original event at t1. The duplications are avoided by NOT counting the sign progressions 0 -> +1, or 0 -> -1 731ca4445c7SIlya Fursov as brackets. Tracking (instead of recalculating) the sign values makes this procedure work more consistently. 732ca4445c7SIlya Fursov 733ca4445c7SIlya Fursov The sign values are however recalculated if the postevent() callback has changed the current solution vector U 734ca4445c7SIlya Fursov (such a change resets everything). 735ca4445c7SIlya Fursov The sign value is also set to zero if the dt_min criterion has triggered the event. This allows the algorithm to 736ca4445c7SIlya Fursov work consistently, irrespective of the type of criterion involved (vtol/dt_min). 737ca4445c7SIlya Fursov 738ca4445c7SIlya Fursov =Event from min bracket= 739ca4445c7SIlya Fursov When the event handler ends up with a bracket [t0, t1] with size <= dt_min, a zero crossing is reported at t1, 740ca4445c7SIlya Fursov and never at t0. If such a bracket is discovered when TS is staying at t0, one more step forward (to t1) is necessary 741ca4445c7SIlya Fursov to mark the found event. This is the situation of revisiting t1, which is described below (see Revisiting). 742ca4445c7SIlya Fursov 743ca4445c7SIlya Fursov Why t0 is not reported as event location? Suppose it is, and let f0 < 0, f1 > 0. Also suppose that the 744ca4445c7SIlya Fursov postevent() handler has slightly changed the solution U, so the sign at t0 is recalculated: it equals -1. As the TS steps 745ca4445c7SIlya Fursov further: t0 -> t2, with sign0 == -1, and sign2 == +1, the event handler will locate the bracket [t0, t2], eventually 746ca4445c7SIlya Fursov resolving a new event near t1, i.e. finding a duplicate event. 747ca4445c7SIlya Fursov This situation is avoided by reporting the event at t1 in the first place. 748ca4445c7SIlya Fursov 749ca4445c7SIlya Fursov =Revisiting= 750ca4445c7SIlya Fursov When handling the situation with small bracket size, the TS solver may happen to visit the same point twice, 751ca4445c7SIlya Fursov but with different results. 752ca4445c7SIlya Fursov 753ca4445c7SIlya Fursov E.g. originally it discovered a bracket with sign change [t0, t10], and started resolving the zero-crossing, 754ca4445c7SIlya Fursov visiting the points t1,...,t9 : t0 < t1 < ... < t9 < t10. Suppose that at t9 the algorithm discovers 755ca4445c7SIlya Fursov that [t9, t10] is a bracket with the sign change it was looking for, and that |t10 - t9| is too small. 756ca4445c7SIlya Fursov So point t10 should be revisited and marked as the zero crossing (by the minimum bracket size criterion). 757ca4445c7SIlya Fursov On re-visiting t10, via the refined sequence of steps t0,...,t10, the TS solver may arrive at a solution U* 758ca4445c7SIlya Fursov different from the solution U it found at t10 originally. Hence, the indicator functions at t10 may become different, 759ca4445c7SIlya Fursov and the condition of the sign change, which existed originally, may disappear, breaking the logic of the algorithm. 760ca4445c7SIlya Fursov 761ca4445c7SIlya Fursov To handle such (-=unlikely=-, but possible) situations, two strategies can be considered: 762ca4445c7SIlya Fursov 1) [not used here] Allow the brackets with sign change to disappear during iterations. The algorithm should be able 763ca4445c7SIlya Fursov to cleanly exit the iteration and leave all the objects/variables/caches involved in a valid state. 764ca4445c7SIlya Fursov 2) [ADOPTED HERE!] On revisiting t10, the event handler reuses the indicator functions previously calculated for the 765ca4445c7SIlya Fursov original solution U. This U may be less precise than U*, but this trick does not allow the algorithm logic to break down. 766ca4445c7SIlya Fursov HOWEVER, the original U is not stored anywhere, it is essentially lost since the TS performed the rollback from it. 767ca4445c7SIlya Fursov On revisiting t10, the updated solution U* will inevitably be found and used everywhere EXCEPT the current 768ca4445c7SIlya Fursov indicator functions calculation, e.g. U* will be used in the postevent() handler call. Since t10 is the event location, 769ca4445c7SIlya Fursov the appropriate indicator-function-signs will be enforced to be 0 (regardless if the solution was U or U*). 770ca4445c7SIlya Fursov If the solution is then changed by the postevent(), the indicator-function-signs will be recalculated. 771ca4445c7SIlya Fursov 772ca4445c7SIlya Fursov Whether the algorithm is revisiting a point in the current TSEventHandler() call is flagged by 'event->revisit_right'. 773ca4445c7SIlya Fursov */ 774d71ae5a4SJacob Faibussowitsch PetscErrorCode TSEventHandler(TS ts) 775d71ae5a4SJacob Faibussowitsch { 7766427ac75SLisandro Dalcin TSEvent event; 777ca4445c7SIlya Fursov PetscReal t, dt_next = 0.0; 7782dc7a7e3SShri Abhyankar Vec U; 779ca4445c7SIlya Fursov PetscInt minsidein = 2, minsideout = 2; // minsideout is sync on all ranks 780ca4445c7SIlya Fursov PetscBool finished = PETSC_FALSE; // should stay sync on all ranks 781ca4445c7SIlya Fursov PetscBool revisit_right_cache; // [sync] flag for inner consistency checks 7822dc7a7e3SShri Abhyankar 7832dc7a7e3SShri Abhyankar PetscFunctionBegin; 7846427ac75SLisandro Dalcin PetscValidHeaderSpecific(ts, TS_CLASSID, 1); 785ca4445c7SIlya Fursov 7863ba16761SJacob Faibussowitsch if (!ts->event) PetscFunctionReturn(PETSC_SUCCESS); 7876427ac75SLisandro Dalcin event = ts->event; 788ca4445c7SIlya Fursov event->nevents_zero = 0; 789ca4445c7SIlya Fursov revisit_right_cache = event->revisit_right; 790ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->nevents; i++) event->side[i] = 2; // side's are reset on each new iteration 791ca4445c7SIlya Fursov if (event->iterctr == 0) 792ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->nevents; i++) event->justrefined_AB[i] = PETSC_FALSE; 7932dc7a7e3SShri Abhyankar 7949566063dSJacob Faibussowitsch PetscCall(TSGetTime(ts, &t)); 795ca4445c7SIlya Fursov if (!event->processing) { // update the caches 796ca4445c7SIlya Fursov PetscReal dt; 7979566063dSJacob Faibussowitsch PetscCall(TSGetTimeStep(ts, &dt)); 798ca4445c7SIlya Fursov event->ptime_cache = t; 799ca4445c7SIlya Fursov event->timestep_cache = dt; // the next TS move is planned to be: t -> t+dt 8002dc7a7e3SShri Abhyankar } 801*fe4ad979SIlya Fursov if (event->processing && event->iterctr == 0 && Not_PETSC_DECIDE(event->timestep_2nd_postevent)) { // update the caches while processing the post-event steps 802*fe4ad979SIlya Fursov event->ptime_cache = t; 803*fe4ad979SIlya Fursov event->timestep_cache = event->timestep_2nd_postevent; 804*fe4ad979SIlya Fursov } 8052dc7a7e3SShri Abhyankar 806ca4445c7SIlya Fursov PetscCall(TSGetSolution(ts, &U)); // if revisiting, this will be the updated U* (see discussion on "Revisiting" in the Developer notes above) 807ca4445c7SIlya Fursov if (event->revisit_right) { 808ca4445c7SIlya Fursov PetscReal tr = TSEvent_update_from_right(event); 809ca4445c7SIlya Fursov PetscCheck(PetscAbsReal(tr - t) < PETSC_SMALL, PetscObjectComm((PetscObject)ts), PETSC_ERR_PLIB, "Inconsistent time value when performing 'revisiting' in TSEventHandler()"); 8102dc7a7e3SShri Abhyankar } else { 811ca4445c7SIlya Fursov PetscCall(VecLockReadPush(U)); 812ca4445c7SIlya Fursov PetscCallBack("TSEvent indicator", (*event->indicator)(ts, t, U, event->fvalue, event->ctx)); // fill fvalue's at point 't' 813ca4445c7SIlya Fursov PetscCall(VecLockReadPop(U)); 814ca4445c7SIlya Fursov TSEventCalcSigns(event->nevents, event->fvalue, event->vtol, event->fsign); // fill fvalue signs 815ca4445c7SIlya Fursov } 816ca4445c7SIlya Fursov PetscCall(TSEventTestZero(ts, t)); // check if the current point 't' is the event location; event->side[] may get updated 817ca4445c7SIlya Fursov 818ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->nevents; i++) { // check for brackets on the left/right of 't' 819ca4445c7SIlya Fursov if (event->side[i] != 0) event->side[i] = TSEventTestBracket(event->fsign_prev[i], event->fsign[i], event->fsign_right[i], event->direction[i], event->iterctr); 820ca4445c7SIlya Fursov minsidein = PetscMin(minsidein, event->side[i]); 821ca4445c7SIlya Fursov } 822ca4445c7SIlya Fursov PetscCall(MPIU_Allreduce(&minsidein, &minsideout, 1, MPIU_INT, MPI_MIN, PetscObjectComm((PetscObject)ts))); 823ca4445c7SIlya Fursov /* 824ca4445c7SIlya Fursov minsideout (sync on all ranks) indicates the minimum of the following states: 825ca4445c7SIlya Fursov -1 : [ptime_prev, t] is a bracket for some indicator-function-i 826ca4445c7SIlya Fursov +1 : [t, ptime_right] is a bracket for some indicator-function-i 827ca4445c7SIlya Fursov 0 : t is a zero-crossing for some indicator-function-i 828ca4445c7SIlya Fursov 2 : none of the above 829ca4445c7SIlya Fursov */ 830ca4445c7SIlya Fursov PetscCheck(!event->revisit_right || minsideout == 0, PetscObjectComm((PetscObject)ts), PETSC_ERR_PLIB, "minsideout != 0 when performing 'revisiting' in TSEventHandler()"); 831ca4445c7SIlya Fursov 832ca4445c7SIlya Fursov if (minsideout == -1 || minsideout == +1) { // this if-branch will refine the left/right bracket 833ca4445c7SIlya Fursov const PetscReal bracket_size = (minsideout == -1) ? t - event->ptime_prev : event->ptime_right - t; // sync on all ranks 834ca4445c7SIlya Fursov 835ca4445c7SIlya Fursov if (minsideout == +1 && bracket_size <= event->timestep_min) { // check if the bracket (right) is small 836ca4445c7SIlya Fursov // [--------------------|-] 837ca4445c7SIlya Fursov dt_next = bracket_size; // need one more step to get to event->ptime_right 838ca4445c7SIlya Fursov event->revisit_right = PETSC_TRUE; 839ca4445c7SIlya Fursov TSEvent_update_left(event, t); 840ca4445c7SIlya Fursov if (event->monitor) 841ca4445c7SIlya Fursov PetscCall(PetscViewerASCIIPrintf(event->monitor, "[%d] TSEvent: iter %" PetscInt_FMT " - reached too small bracket [%g - %g], next stepping to its right end %g (revisiting)\n", PetscGlobalRank, event->iterctr, (double)event->ptime_prev, 842ca4445c7SIlya Fursov (double)event->ptime_right, (double)(event->ptime_prev + dt_next))); 843ca4445c7SIlya Fursov } else { // the bracket is not very small -> refine it 844ca4445c7SIlya Fursov // [--------|-------------] 845ca4445c7SIlya Fursov if (bracket_size <= 2 * event->timestep_min) dt_next = bracket_size / 2; // the bracket is almost small -> bisect it 846ca4445c7SIlya Fursov else { // the bracket is not small -> use Anderson-Bjorck 847ca4445c7SIlya Fursov PetscReal dti_min = PETSC_MAX_REAL; 848ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->nevents; i++) { 849ca4445c7SIlya Fursov if (event->side[i] == minsideout) { // only refine the appropriate brackets 850ca4445c7SIlya Fursov PetscReal dti = RefineAndersonBjorck(event->ptime_prev, t, event->ptime_right, event->fvalue_prev[i], event->fvalue[i], event->fvalue_right[i], event->side[i], &event->side_prev[i], event->justrefined_AB[i], &event->gamma_AB[i]); 851ca4445c7SIlya Fursov dti_min = PetscMin(dti_min, dti); 852ca4445c7SIlya Fursov } 853ca4445c7SIlya Fursov } 854ca4445c7SIlya Fursov PetscCall(MPIU_Allreduce(&dti_min, &dt_next, 1, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)ts))); 855ca4445c7SIlya Fursov if (dt_next < event->timestep_min) dt_next = event->timestep_min; 856ca4445c7SIlya Fursov if (bracket_size - dt_next < event->timestep_min) dt_next = bracket_size - event->timestep_min; 857ca4445c7SIlya Fursov } 858ca4445c7SIlya Fursov 859ca4445c7SIlya Fursov if (minsideout == -1) { // minsideout == -1, update the right-end values, retain the left-end values 860ca4445c7SIlya Fursov TSEvent_update_right(event, t); 861ca4445c7SIlya Fursov PetscCall(TSRollBack(ts)); 862ca4445c7SIlya Fursov PetscCall(TSSetConvergedReason(ts, TS_CONVERGED_ITERATING)); // e.g. to override TS_CONVERGED_TIME on reaching ts->max_time 863ca4445c7SIlya Fursov } else TSEvent_update_left(event, t); // minsideout == +1, update the left-end values, retain the right-end values 864ca4445c7SIlya Fursov 865ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->nevents; i++) { // update the "Anderson-Bjorck" flags 866ca4445c7SIlya Fursov if (event->side[i] == minsideout) { 867ca4445c7SIlya Fursov event->justrefined_AB[i] = PETSC_TRUE; // only for these i's Anderson-Bjorck was invoked 868ca4445c7SIlya Fursov if (event->monitor) 869ca4445c7SIlya Fursov PetscCall(PetscViewerASCIIPrintf(event->monitor, "[%d] TSEvent: iter %" PetscInt_FMT " - Event %" PetscInt_FMT " refining the bracket with sign change [%g - %g], next stepping to %g\n", PetscGlobalRank, event->iterctr, i, (double)event->ptime_prev, 870ca4445c7SIlya Fursov (double)event->ptime_right, (double)(event->ptime_prev + dt_next))); 871ca4445c7SIlya Fursov } else event->justrefined_AB[i] = PETSC_FALSE; // for these i's Anderson-Bjorck was not invoked 872ca4445c7SIlya Fursov } 873ca4445c7SIlya Fursov } 874ca4445c7SIlya Fursov event->iterctr++; 875ca4445c7SIlya Fursov event->processing = PETSC_TRUE; 876ca4445c7SIlya Fursov } else if (minsideout == 0) { // found the appropriate zero-crossing (and no brackets to the left), finishing! 877ca4445c7SIlya Fursov // [--------0-------------] 878ca4445c7SIlya Fursov finished = PETSC_TRUE; 879ca4445c7SIlya Fursov event->revisit_right = PETSC_FALSE; 880ca4445c7SIlya Fursov for (PetscInt i = 0; i < event->nevents; i++) 881ca4445c7SIlya Fursov if (event->side[i] == minsideout) { 882ca4445c7SIlya Fursov event->events_zero[event->nevents_zero++] = i; 883ca4445c7SIlya Fursov if (event->fsign[i] == 0) { // vtol was engaged 884ca4445c7SIlya Fursov if (event->monitor) 885ca4445c7SIlya Fursov PetscCall(PetscViewerASCIIPrintf(event->monitor, "[%d] TSEvent: iter %" PetscInt_FMT " - Event %" PetscInt_FMT " zero crossing located at time %g (tol=%g)\n", PetscGlobalRank, event->iterctr, i, (double)t, (double)event->vtol[i])); 886ca4445c7SIlya Fursov } else { // dt_min was engaged 887ca4445c7SIlya Fursov event->fsign[i] = 0; // sign = 0 is enforced further 888ca4445c7SIlya Fursov if (event->monitor) 889ca4445c7SIlya Fursov PetscCall(PetscViewerASCIIPrintf(event->monitor, "[%d] TSEvent: iter %" PetscInt_FMT " - Event %" PetscInt_FMT " accepting time %g as event location, due to reaching too small bracket [%g - %g]\n", PetscGlobalRank, event->iterctr, i, (double)t, 890ca4445c7SIlya Fursov (double)event->ptime_prev, (double)t)); 891ca4445c7SIlya Fursov } 892ca4445c7SIlya Fursov } 893ca4445c7SIlya Fursov event->iterctr++; 894ca4445c7SIlya Fursov event->processing = PETSC_TRUE; 895ca4445c7SIlya Fursov } else { // minsideout == 2: no brackets, no zero-crossings 896ca4445c7SIlya Fursov // [----------------------] 897ca4445c7SIlya Fursov PetscCheck(event->iterctr == 0, PetscObjectComm((PetscObject)ts), PETSC_ERR_PLIB, "Unexpected state (event->iterctr != 0) in TSEventHandler()"); 898*fe4ad979SIlya Fursov if (event->processing) { 899*fe4ad979SIlya Fursov PetscReal dt2; 900*fe4ad979SIlya Fursov if (event->timestep_2nd_postevent == PETSC_DECIDE) dt2 = event->timestep_cache; // (1) 901*fe4ad979SIlya Fursov else dt2 = event->timestep_2nd_postevent; // (2), (2a), (3) 902*fe4ad979SIlya Fursov PetscCall(TSSetTimeStep(ts, TSEvent_dt_cap(ts, t, dt2, Not_PETSC_DECIDE(event->timestep_2nd_postevent)))); // set the second post-event step 903*fe4ad979SIlya Fursov } 904ca4445c7SIlya Fursov event->processing = PETSC_FALSE; 905ca4445c7SIlya Fursov } 906ca4445c7SIlya Fursov 907ca4445c7SIlya Fursov // if 'revisit_right' was flagged before the current iteration started, the iteration is expected to finish 908ca4445c7SIlya Fursov PetscCheck(!revisit_right_cache || finished, PetscObjectComm((PetscObject)ts), PETSC_ERR_PLIB, "Unexpected state of 'revisit_right_cache' in TSEventHandler()"); 909ca4445c7SIlya Fursov 910ca4445c7SIlya Fursov if (finished) { // finished handling the current event 911ca4445c7SIlya Fursov PetscCall(TSPostEvent(ts, t, U)); 912ca4445c7SIlya Fursov 913*fe4ad979SIlya Fursov PetscReal dt1; 914*fe4ad979SIlya Fursov if (event->timestep_postevent == PETSC_DECIDE) { // (1), (2) 915*fe4ad979SIlya Fursov dt1 = event->ptime_cache - t; 916ca4445c7SIlya Fursov event->processing = PETSC_TRUE; 917*fe4ad979SIlya Fursov if (PetscAbsReal(dt1) < PETSC_SMALL) { // (1a), (2a): the cached post-event point == event point 918*fe4ad979SIlya Fursov dt1 = event->timestep_cache; 919*fe4ad979SIlya Fursov event->processing = Not_PETSC_DECIDE(event->timestep_2nd_postevent); 920ca4445c7SIlya Fursov } 921*fe4ad979SIlya Fursov } else { // (3), (4) 922*fe4ad979SIlya Fursov dt1 = event->timestep_postevent; // 1st post-event dt = user-provided value 923*fe4ad979SIlya Fursov event->processing = Not_PETSC_DECIDE(event->timestep_2nd_postevent); 924ca4445c7SIlya Fursov } 925*fe4ad979SIlya Fursov 926*fe4ad979SIlya Fursov PetscCall(TSSetTimeStep(ts, TSEvent_dt_cap(ts, t, dt1, Not_PETSC_DECIDE(event->timestep_postevent)))); // set the first post-event step 927ca4445c7SIlya Fursov event->iterctr = 0; 928ca4445c7SIlya Fursov } // if-finished 929ca4445c7SIlya Fursov 930ca4445c7SIlya Fursov if (event->iterctr == 0) TSEvent_update_left(event, t); // not found an event, or finished the event 931ca4445c7SIlya Fursov else { 932ca4445c7SIlya Fursov PetscCall(TSGetTime(ts, &t)); // update 't' to account for potential rollback 933ca4445c7SIlya Fursov PetscCall(TSSetTimeStep(ts, TSEvent_dt_cap(ts, t, dt_next, PETSC_FALSE))); // continue resolving the event 93438bf2713SShri Abhyankar } 9353ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 9362dc7a7e3SShri Abhyankar } 9372dc7a7e3SShri Abhyankar 938d71ae5a4SJacob Faibussowitsch PetscErrorCode TSAdjointEventHandler(TS ts) 939d71ae5a4SJacob Faibussowitsch { 9406427ac75SLisandro Dalcin TSEvent event; 941d0578d90SShri Abhyankar PetscReal t; 942d0578d90SShri Abhyankar Vec U; 943d0578d90SShri Abhyankar PetscInt ctr; 944ca4445c7SIlya Fursov PetscBool forwardsolve = PETSC_FALSE; // Flag indicating that TS is doing an adjoint solve 945d0578d90SShri Abhyankar 946d0578d90SShri Abhyankar PetscFunctionBegin; 9476427ac75SLisandro Dalcin PetscValidHeaderSpecific(ts, TS_CLASSID, 1); 9483ba16761SJacob Faibussowitsch if (!ts->event) PetscFunctionReturn(PETSC_SUCCESS); 9496427ac75SLisandro Dalcin event = ts->event; 950d0578d90SShri Abhyankar 9519566063dSJacob Faibussowitsch PetscCall(TSGetTime(ts, &t)); 9529566063dSJacob Faibussowitsch PetscCall(TSGetSolution(ts, &U)); 953d0578d90SShri Abhyankar 954d0578d90SShri Abhyankar ctr = event->recorder.ctr - 1; 955bcbf8bb3SShri Abhyankar if (ctr >= 0 && PetscAbsReal(t - event->recorder.time[ctr]) < PETSC_SMALL) { 956ca4445c7SIlya Fursov // Call the user post-event function 957d0578d90SShri Abhyankar if (event->postevent) { 958ca4445c7SIlya Fursov PetscCallBack("TSEvent post-event processing", (*event->postevent)(ts, event->recorder.nevents[ctr], event->recorder.eventidx[ctr], t, U, forwardsolve, event->ctx)); 959d0578d90SShri Abhyankar event->recorder.ctr--; 960d0578d90SShri Abhyankar } 961d0578d90SShri Abhyankar } 9623ba16761SJacob Faibussowitsch PetscCall(PetscBarrier((PetscObject)ts)); 9633ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 964d0578d90SShri Abhyankar } 9651ea83e56SMiguel 9661ea83e56SMiguel /*@ 967ca4445c7SIlya Fursov TSGetNumEvents - Get the number of events defined on the given MPI process 9681ea83e56SMiguel 9691ea83e56SMiguel Logically Collective 9701ea83e56SMiguel 9714165533cSJose E. Roman Input Parameter: 972bcf0153eSBarry Smith . ts - the `TS` context 9731ea83e56SMiguel 9744165533cSJose E. Roman Output Parameter: 975ca4445c7SIlya Fursov . nevents - the number of local events on each MPI process 9761ea83e56SMiguel 9771ea83e56SMiguel Level: intermediate 9781ea83e56SMiguel 9791cc06b55SBarry Smith .seealso: [](ch_ts), `TSEvent`, `TSSetEventHandler()` 9801ea83e56SMiguel @*/ 981d71ae5a4SJacob Faibussowitsch PetscErrorCode TSGetNumEvents(TS ts, PetscInt *nevents) 982d71ae5a4SJacob Faibussowitsch { 9831ea83e56SMiguel PetscFunctionBegin; 9841ea83e56SMiguel *nevents = ts->event->nevents; 9853ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 9861ea83e56SMiguel } 987