Event Handling and Callback Functions

ModelingToolkit provides several ways to represent system events, which enable system state or parameters to be changed when certain conditions are satisfied, or can be used to detect discontinuities. These events are ultimately converted into DifferentialEquations.jl ContinuousCallbacks or DiscreteCallbacks, or into more specialized callback types from the DiffEqCallbacks.jl library.

ODESystems and SDESystems accept keyword arguments continuous_events and discrete_events to symbolically encode continuous or discrete callbacks. JumpSystems currently support only discrete_events. Continuous events are applied when a given condition becomes zero, with root finding used to determine the time at which a zero crossing occurred. Discrete events are applied when a condition tested after each timestep evaluates to true. See the DifferentialEquations docs for more detail.

Events involve both a condition function (for the zero crossing or truth test), and an affect function (for determining how to update the system when the event occurs). These can both be specified symbolically, but a more general functional affect representation is also allowed as described below.

Continuous Events

The basic purely symbolic continuous event interface to encode one continuous event is

AbstractSystem(eqs, ...; continuous_events::Vector{Equation})
AbstractSystem(eqs, ...; continuous_events::Pair{Vector{Equation}, Vector{Equation}})

In the former, equations that evaluate to 0 will represent conditions that should be detected by the integrator, for example to force stepping to times of discontinuities. The latter allow modeling of events that have an effect on the state, where the first entry in the Pair is a vector of equations describing event conditions, and the second vector of equations describe the effect on the state. Each affect equation must be of the form

single_state_variable ~ expression_involving_any_variables_or_parameters

or

single_parameter ~ expression_involving_any_variables_or_parameters

In this basic interface, multiple variables can be changed in one event, or multiple parameters, but not a mix of parameters and variables. The latter can be handled via more general functional affects.

Finally, multiple events can be encoded via a Vector{Pair{Vector{Equation}, Vector{Equation}}}.

Example: Friction

The system below illustrates how continuous events can be used to model Coulomb friction

using ModelingToolkit, OrdinaryDiffEq, Plots
function UnitMassWithFriction(k; name)
  @variables t x(t)=0 v(t)=0
  D = Differential(t)
  eqs = [
    D(x) ~ v
    D(v) ~ sin(t) - k*sign(v) # f = ma, sinusoidal force acting on the mass, and Coulomb friction opposing the movement
  ]
  ODESystem(eqs, t; continuous_events=[v ~ 0], name) # when v = 0 there is a discontinuity
end
@named m = UnitMassWithFriction(0.7)
prob = ODEProblem(m, Pair[], (0, 10pi))
sol = solve(prob, Tsit5())
plot(sol)