1 #ifndef SimTK_SimTKCOMMON_EVENT_H_ 2 #define SimTK_SimTKCOMMON_EVENT_H_ 3 4 /* -------------------------------------------------------------------------- * 5 * Simbody(tm): SimTKcommon * 6 * -------------------------------------------------------------------------- * 7 * This is part of the SimTK biosimulation toolkit originating from * 8 * Simbios, the NIH National Center for Physics-Based Simulation of * 9 * Biological Structures at Stanford, funded under the NIH Roadmap for * 10 * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody. * 11 * * 12 * Portions copyright (c) 2008-12 Stanford University and the Authors. * 13 * Authors: Michael Sherman * 14 * Contributors: * 15 * * 16 * Licensed under the Apache License, Version 2.0 (the "License"); you may * 17 * not use this file except in compliance with the License. You may obtain a * 18 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. * 19 * * 20 * Unless required by applicable law or agreed to in writing, software * 21 * distributed under the License is distributed on an "AS IS" BASIS, * 22 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 23 * See the License for the specific language governing permissions and * 24 * limitations under the License. * 25 * -------------------------------------------------------------------------- */ 26 27 /** @file 28 * 29 * This file declares the types needed for Simbody's support for Events. 30 */ 31 32 #include "SimTKcommon/basics.h" 33 34 namespace SimTK { 35 36 /** @class SimTK::EventId 37 This is a class to represent unique IDs for events in a type-safe way. These 38 are created and managed by the System, not the State. **/ 39 SimTK_DEFINE_UNIQUE_INDEX_TYPE(EventId); 40 41 /** @class SimTK::SystemEventTriggerIndex 42 This unique integer type is for identifying a triggered event in the full 43 System-level view of the State. More precisely, this is the index of the slot 44 in the global array in the cache allocated to hold the value of that event's 45 trigger function. 46 @see EventTriggerIndex **/ 47 SimTK_DEFINE_UNIQUE_INDEX_TYPE(SystemEventTriggerIndex); 48 49 /** @class SimTK::SystemEventTriggerByStageIndex 50 This unique integer type is for identifying a triggered event within a 51 particular Stage of the full System-level view of the State. (Event triggers 52 for a particular Stage are stored consecutively within the full collection of 53 event triggers.) That is, the EventTriggerByStageIndex will be 0 for the first 54 event trigger at that stage. 55 @see EventTriggerByStageIndex 56 **/ 57 SimTK_DEFINE_UNIQUE_INDEX_TYPE(SystemEventTriggerByStageIndex); 58 59 /** @class SimTK::EventTriggerByStageIndex 60 Unique integer type for Subsystem-local, per-stage event indexing. 61 @see SystemEventTriggerByStageIndex **/ 62 SimTK_DEFINE_UNIQUE_INDEX_TYPE(EventTriggerByStageIndex); 63 64 /** An Event is "something that happens" during a Study that is advancing 65 through time. Its occurrence interrupts the normal flow of computation, allowing 66 an event Handler to adjust the State prior to resuming the Study. 67 68 Events are allocated by Subsystems, but require some System global resources. 69 All Events are given a unique EventId. Some Events require other State 70 resources, such as slots for the values of trigger functions in the case of 71 Triggered events. 72 73 Events can be allocated at Topology, Model, and Instance Stages. All Event 74 resources are assigned when the Instance stage is realized. However, if an 75 Event requires state variables, then it must be allocated by Model stage. **/ 76 class Event { 77 public: 78 79 /** These are all the possible causes for events. 80 81 @par Initialization 82 A Study has performed its own initialization and is about to start. 83 84 @par Triggered 85 An event trigger function underwent a monitored sign transition. 86 87 @par Scheduled 88 An integrator reached a previously-scheduled time for the Event to occur. 89 90 @par TimeAdvanced 91 An integrator completed an internal step, meaning that it has reached 92 a point where time has advanced irreversibly. 93 94 @par Signaled 95 A flag in the State has been explicitly set, meaning that a particular 96 Event has occurred. Anyone with write access to a State can set these, 97 but typically they are set in event handlers associated with one of 98 the other kinds of events. 99 100 @par Termination 101 The Study has finished. If a Termination event handler signals more 102 Events, those signaled events are not processed by the Study; that is, 103 the signals remain set in the final State. 104 105 In case several of these causes are detected in a single step, they are 106 sequentialized in the order shown, like this: 107 108 1. The occurrence of triggered events is reported and the triggering state 109 and a list of triggered events are passed to the event handler for 110 processing (meaning the state, but not the time, is modified). Note 111 that simultaneity *within* the set of triggered events may also require 112 special handling; we're not talking about that here, just simultaneity 113 of *causes*. 114 2. Next, using the state resulting from step 1, the time is checked to see 115 if scheduled events have occurred. If so, a list of those events is 116 passed to the event handler for processing. 117 3. Next, if this system has requested time-advanced events, the event 118 handler is called with the state that resulted from step 2 and the "time 119 advanced" cause noted. No event list is passed in that case. The state 120 may be modified. 121 4. Last, if the final time has been reached or if any of the event handlers 122 asked for termination, we pass the state to the event handler again 123 noting that we have reached termination. The state may be modified and 124 the result will be the final state of the simulation. 125 **/ 126 class Cause { 127 public: 128 enum Num { 129 Initialization = 1, 130 Triggered = 2, 131 Scheduled = 3, 132 TimeAdvanced = 4, 133 Signaled = 5, 134 Termination = 6, 135 Invalid = -1 136 }; 137 Cause()138 Cause() : value(Invalid) {} Cause(Num n)139 Cause(Num n) : value(n) {} // implicit conversion Num()140 operator Num() const {return value;} // implicit conversion 141 Cause& operator=(Num n) {value=n; return *this;} 142 isValid()143 bool isValid() const {return Initialization<=value && value<=Termination;} 144 145 private: 146 Num value; 147 }; 148 149 /** This is useful for debugging; it translates an Event::Cause into a 150 readable string. **/ 151 SimTK_SimTKCOMMON_EXPORT static const char* getCauseName(Cause); 152 153 154 /** Triggered Events respond to zero crossings of their associated trigger 155 function. This enum defines constants for use in specifying which kind 156 of zero crossing has been seen, or which kinds are considered interesting. 157 For the latter purpose, these can be or'ed together to make a mask. **/ 158 enum Trigger { 159 NoEventTrigger =0x0000, // must be 0 160 161 PositiveToNegative =0x0001, // 1 162 NegativeToPositive =0x0002, // 2 163 164 Falling =(PositiveToNegative), // 1 165 Rising =(NegativeToPositive), // 2 166 AnySignChange =(PositiveToNegative|NegativeToPositive) // 3 167 }; 168 169 /** This is useful for debugging; it translates an Event::Trigger or a mask 170 formed by a union of Event::Triggers, into a readable string. **/ 171 SimTK_SimTKCOMMON_EXPORT static std::string eventTriggerString(Trigger); 172 173 174 /** Classify a before/after sign transition. Before and after must both 175 be -1,0, or 1 as returned by the SimTK::sign() function applied to 176 the trigger function value at the beginning and end of a step. **/ classifyTransition(int before,int after)177 static Trigger classifyTransition(int before, int after) { 178 if (before==after) 179 return NoEventTrigger; 180 if (before==0) 181 return NoEventTrigger; // Do not report transitions away from zero. 182 if (before==1) 183 return PositiveToNegative; 184 // before==-1 185 return NegativeToPositive; 186 } 187 188 /** Given an observed transition, weed out ignorable ones using the supplied 189 mask. That is, the return will indicate NoEventTrigger unless the original 190 Trigger was present in the mask. **/ maskTransition(Trigger transition,Trigger mask)191 static Trigger maskTransition(Trigger transition, Trigger mask) { 192 // we're depending on NoEventTrigger==0 193 return Trigger(transition & mask); 194 } 195 196 private: 197 }; 198 199 200 /** This class is used to communicate between the System and an Integrator 201 regarding the properties of a particular event trigger function. Currently 202 these are: 203 - Whether to watch for rising sign transitions, falling, or both. [BOTH] 204 - Whether to watch for transitions to and from zero. [NO] 205 - The localization window in units of the System's timescale. [10%] 206 (That is then the "unit" window which is reduced by the accuracy setting.) 207 208 The default values are shown in brackets above. **/ 209 class SimTK_SimTKCOMMON_EXPORT EventTriggerInfo { 210 public: 211 EventTriggerInfo(); 212 explicit EventTriggerInfo(EventId eventId); 213 ~EventTriggerInfo(); 214 EventTriggerInfo(const EventTriggerInfo&); 215 EventTriggerInfo& operator=(const EventTriggerInfo&); 216 217 EventId getEventId() const; // returns -1 if not set 218 bool shouldTriggerOnRisingSignTransition() const; // default=true 219 bool shouldTriggerOnFallingSignTransition() const; // default=true 220 Real getRequiredLocalizationTimeWindow() const; // default=0.1 221 222 // These return the modified 'this', like assignment operators. 223 EventTriggerInfo& setEventId(EventId); 224 EventTriggerInfo& setTriggerOnRisingSignTransition(bool); 225 EventTriggerInfo& setTriggerOnFallingSignTransition(bool); 226 EventTriggerInfo& setRequiredLocalizationTimeWindow(Real); 227 calcTransitionMask()228 Event::Trigger calcTransitionMask() const { 229 unsigned mask = 0; 230 if (shouldTriggerOnRisingSignTransition()) { 231 mask |= Event::NegativeToPositive; 232 } 233 if (shouldTriggerOnFallingSignTransition()) { 234 mask |= Event::PositiveToNegative; 235 } 236 return Event::Trigger(mask); 237 } 238 calcTransitionToReport(Event::Trigger transitionSeen)239 Event::Trigger calcTransitionToReport 240 (Event::Trigger transitionSeen) const 241 { 242 // report -1 to 1 or 1 to -1 as appropriate 243 if (transitionSeen & Event::Rising) 244 return Event::NegativeToPositive; 245 if (transitionSeen & Event::Falling) 246 return Event::PositiveToNegative; 247 assert(!"impossible event transition situation"); 248 return Event::NoEventTrigger; 249 } 250 251 private: 252 class EventTriggerInfoRep; 253 254 // opaque implementation for binary compatibility 255 EventTriggerInfoRep* rep; 256 getRep()257 const EventTriggerInfoRep& getRep() const {assert(rep); return *rep;} updRep()258 EventTriggerInfoRep& updRep() {assert(rep); return *rep;} 259 }; 260 261 262 263 264 //============================================================================== 265 // HANDLE EVENTS OPTIONS and HANDLE EVENTS RESULTS 266 //============================================================================== 267 /** Options for the handleEvent() method. Accuracy should be be set by the 268 caller, but if not the default is 1e-4. **/ 269 class HandleEventsOptions { 270 public: 271 enum Option { 272 /** Take all defaults. **/ 273 None = 0x0000, 274 /** Normally failure to meet the accuracy requirements throws an 275 exception. This will force the handleEvent() method to quietly return bad 276 status instead. **/ 277 DontThrow = 0x0001, 278 /** Use the stricter infinity (max absolute value) norm rather than 279 the default RMS norm to determine when accuracy has been achieved. **/ 280 UseInfinityNorm = 0x0002 281 }; 282 283 HandleEventsOptions()284 HandleEventsOptions() {clear();} HandleEventsOptions(Real accuracy)285 explicit HandleEventsOptions(Real accuracy) 286 { clear(); setAccuracy(accuracy); } HandleEventsOptions(Option opt)287 explicit HandleEventsOptions(Option opt) 288 { clear(); setOption(opt); } 289 290 /** Restore this object to its default-constructed state (no options 291 selected, default accuracy). A reference to the 292 newly-cleared object is returned. **/ clear()293 HandleEventsOptions& clear() 294 { optionSet=0; setAccuracyDefaults(); return *this; } 295 296 /** The norm of the constraint errors must be driven to below this value 297 for a project() to be considered successful. Normally an RMS norm is used 298 but you can override that to use an infinity norm instead. **/ setAccuracy(Real accuracy)299 HandleEventsOptions& setAccuracy(Real accuracy) { 300 assert(accuracy > 0); 301 requiredAccuracy = accuracy; 302 return *this; 303 } 304 305 /** Remove a given option from the set. Nothing happens if the option wasn't 306 already set. **/ clearOption(Option opt)307 HandleEventsOptions& clearOption(Option opt) 308 { optionSet &= ~(unsigned)opt; return *this; } 309 /** Select a given option from the set. Nothing happens if the option wasn't 310 already set. **/ setOption(Option opt)311 HandleEventsOptions& setOption (Option opt) 312 { optionSet |= (unsigned)opt; return *this; } 313 314 /** Return the current value for the accuracy option. **/ getAccuracy()315 Real getAccuracy() const {return requiredAccuracy;} 316 isOptionSet(Option opt)317 bool isOptionSet(Option opt) const {return (optionSet&(unsigned)opt) != 0;} 318 getDefaultAccuracy()319 static Real getDefaultAccuracy() {return Real(1e-4);} 320 321 // Set operators: not, or, and, set difference 322 HandleEventsOptions& operator|=(const HandleEventsOptions& opts) 323 { optionSet |= opts.optionSet; return *this; } 324 HandleEventsOptions& operator&=(const HandleEventsOptions& opts) 325 { optionSet &= opts.optionSet; return *this; } 326 HandleEventsOptions& operator-=(const HandleEventsOptions& opts) 327 { optionSet &= ~opts.optionSet; return *this; } 328 329 HandleEventsOptions& operator|=(Option opt) {setOption(opt); return *this;} 330 HandleEventsOptions& operator-=(Option opt) {clearOption(opt); return *this;} 331 332 private: 333 Real requiredAccuracy; 334 unsigned optionSet; 335 setAccuracyDefaults()336 void setAccuracyDefaults() { 337 requiredAccuracy = getDefaultAccuracy(); 338 } 339 }; 340 341 /** Results returned by the handleEvent() method. In addition to return 342 status, this records the lowest stage in the state that was modified by 343 the handler. The caller can use this to determine how much reinitialization 344 is required before time stepping can proceed. **/ 345 class HandleEventsResults { 346 public: HandleEventsResults()347 HandleEventsResults() : m_lowestModifiedStage(Stage::Infinity) {clear();} 348 349 enum Status { 350 /** This object has not been filled in yet and holds no results. **/ 351 Invalid = -1, 352 /** The handleEvent() operation was successful and time stepping 353 may continue. **/ 354 Succeeded = 0, 355 /** The handleEvent() call was successful but the event requires 356 time stepping to terminate. An explanation may have been placed in 357 the message argument. **/ 358 ShouldTerminate = 1, 359 /** The handleEvent() call was unable to successfully handle the 360 event. This is likely to be a fatal error. A human-readable 361 explanation is in the message argument. **/ 362 Failed = 2 363 }; 364 365 /** Restore this object to its default-constructed state, with the return 366 status set to Invalid. **/ clear()367 HandleEventsResults& clear() { 368 m_exitStatus = Invalid; 369 m_anyChangeMade = false; 370 m_lowestModifiedStage = Stage::Infinity; // i.e., nothing modified 371 m_message.clear(); 372 return *this; 373 } isValid()374 bool isValid() const {return m_exitStatus != Invalid;} getExitStatus()375 Status getExitStatus() const {return m_exitStatus;} 376 getAnyChangeMade()377 bool getAnyChangeMade() const 378 { assert(isValid()); return m_anyChangeMade; } getLowestModifiedStage()379 Stage getLowestModifiedStage() const 380 { assert(isValid()); return m_lowestModifiedStage; } getMessage()381 const String& getMessage() const 382 { assert(isValid()); return m_message; } 383 setExitStatus(Status status)384 HandleEventsResults& setExitStatus(Status status) 385 { m_exitStatus=status; return *this; } setAnyChangeMade(bool changeMade)386 HandleEventsResults& setAnyChangeMade(bool changeMade) 387 { m_anyChangeMade=changeMade; return *this; } setLowestModifiedStage(Stage stage)388 HandleEventsResults& setLowestModifiedStage(Stage stage) 389 { m_lowestModifiedStage=stage; return *this; } setMessage(const String & message)390 HandleEventsResults& setMessage(const String& message) 391 { m_message=message; return *this; } 392 private: 393 Status m_exitStatus; 394 bool m_anyChangeMade; 395 Stage m_lowestModifiedStage; 396 String m_message; 397 }; 398 399 } // namespace SimTK 400 401 #endif // SimTK_SimTKCOMMON_EVENT_H_ 402