Source code for pathsim.events._event
#########################################################################################
##
## EVENT MANAGER CLASS FOR EVENT DETECTION
## (events/_event.py)
##
## Milan Rother 2024
##
#########################################################################################
# IMPORTS ===============================================================================
import inspect
import json
import numpy as np
from .. _constants import EVT_TOLERANCE
from ..utils.serialization import Serializable
# EVENT MANAGER CLASS ===================================================================
[docs]
class Event(Serializable):
"""This is the base class of the event handling system.
Monitors system state by evaluating an event function (func_evt) with scalar output.
.. code-block::
func_evt(time) -> event?
If an event is detected, some action (func_act) is performed on the states of the blocks.
.. code-block::
func_evt(time) == True -> event -> func_act(time)
The methods are structured such that event detection can be separated from event
resolution. This is required for adaptive timestep solvers to approach the event
and only resolve it when the event tolerance ('tolerance') is satisfied.
If no action function (func_act) is specified, the event will only be detected but other
than that, no action will be triggered. For general state monitoring.
Parameters
----------
func_evt : callable
event function, where zeros are events
func_act : callable
action function for event resolution
tolerance : float
tolerance to check if detection is close to actual event
Attributes
----------
_history : tuple[None, float], tuple[float, float], tuple[bool, float]
history of event function evaluation after buffering
_times : list[float]
tracking the event times
_active : bool
flag that sets event active or inactive
"""
def __init__(
self,
func_evt=None,
func_act=None,
tolerance=EVT_TOLERANCE
):
#event detection function
self.func_evt = func_evt
#event action function -> event resolution (must not be callable)
self.func_act = func_act
#tolerance for checking if close to actual event
self.tolerance = tolerance
#event function evaluation and evaluation time history (eval, time)
self._history = None, 0.0
#recording the event times
self._times = []
#flag for active event checking
self._active = True
def __len__(self):
"""
Return the number of detected (or rather resolved) events.
Returns
-------
length : int
number of events detected
"""
return len(self._times)
def __iter__(self):
"""
Yields the recorded times at which events are detected.
"""
for t in self._times:
yield t
def __bool__(self):
return self._active
# external methods ------------------------------------------------------------------
[docs]
def on(self): self._active = True
[docs]
def off(self): self._active = False
[docs]
def reset(self):
"""
Reset the recorded event times. Resetting the history is not
required because of the 'buffer' method. Reactivates event tracking.
"""
self._history = None, 0.0
self._times = []
self._active = True
[docs]
def buffer(self, t):
"""Buffer the event function evaluation before the timestep is
taken and the evaluation time.
Parameters
----------
t : float
evaluation time for buffering history
"""
if self.func_evt is not None:
self._history = self.func_evt(t), t
[docs]
def estimate(self, t):
"""Estimate the time of the next event, based on history or internal schedule.
This improves simulation performance by estimating events before the simulation
step such that fewer steps have to be rejected for event location.
Parameters
----------
t : float
evaluation time for estimation
Returns
-------
float | None
estimated time until next event
"""
return None
[docs]
def detect(self, t):
"""Evaluate the event function and decide if an event has occured.
Can also use the history of the event function evaluation from
before the timestep.
Notes
-----
This does nothing and needs to be implemented for specific events!!!
Parameters
----------
t : float
evaluation time for detection
Returns
-------
detected : bool
was an event detected?
close : bool
are we close to the event?
ratio : float
interpolated event location as ratio of timestep
"""
return False, False, 1.0
[docs]
def resolve(self, t):
"""Resolve the event and record the time (t) at which it occurs.
Resolves event using the action function (func_act) if it is defined.
Otherwise this just marks the location of the event in time.
Parameters
----------
t : float
evaluation time for event resolution
"""
#save the time of event resolution
self._times.append(t)
#action function for event resolution
if self.func_act is not None:
self.func_act(t)