Skip to content

Commit 8605fbe

Browse files
Merge pull request #1729 from isaacsas/event_tutorial
Event basics
2 parents 8c08b36 + 44b712a commit 8605fbe

File tree

4 files changed

+327
-172
lines changed

4 files changed

+327
-172
lines changed

docs/make.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ makedocs(sitename = "ModelingToolkit.jl",
1414
# Other available options are
1515
# :autodocs_block, :cross_references, :docs_block, :eval_block, :example_block, :footnote, :meta_block, :missing_docs, :setup_block
1616
],
17-
format = Documenter.HTML(analytics = "UA-90474609-3",
17+
format = Documenter.HTML(; analytics = "UA-90474609-3",
1818
assets = ["assets/favicon.ico"],
19-
canonical = "https://mtk.sciml.ai/stable/"),
19+
canonical = "https://mtk.sciml.ai/stable/",
20+
prettyurls = (get(ENV, "CI", nothing) == "true")),
2021
pages = pages)
2122

2223
deploydocs(repo = "github.com/SciML/ModelingToolkit.jl.git";

docs/pages.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pages = [
1616
"basics/ContextualVariables.md",
1717
"basics/Variable_metadata.md",
1818
"basics/Composition.md",
19+
"basics/Events.md",
1920
"basics/Validation.md",
2021
"basics/DependencyGraphs.md",
2122
"basics/FAQ.md"],

docs/src/basics/Composition.md

Lines changed: 8 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -248,173 +248,11 @@ solving. In summary: these problems are structurally modified, but could be
248248
more efficient and more stable.
249249

250250
## Components with discontinuous dynamics
251-
When modeling, e.g., impacts, saturations or Coulomb friction, the dynamic equations are discontinuous in either the state or one of its derivatives. This causes the solver to take very small steps around the discontinuity, and sometimes leads to early stopping due to `dt <= dt_min`. The correct way to handle such dynamics is to tell the solver about the discontinuity by means of a root-finding equation. [`ODEsystem`](@ref)s accept a keyword argument `continuous_events`
252-
```
253-
ODESystem(eqs, ...; continuous_events::Vector{Equation})
254-
ODESystem(eqs, ...; continuous_events::Pair{Vector{Equation}, Vector{Equation}})
255-
```
256-
where equations can be added that evaluate to 0 at discontinuities.
257-
258-
To model events that have an effect on the state, provide `events::Pair{Vector{Equation}, Vector{Equation}}` 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. The effect equations must be of the form
259-
```
260-
single_state_variable ~ expression_involving_any_variables
261-
```
262-
263-
### Example: Friction
264-
The system below illustrates how this can be used to model Coulomb friction
265-
```@example events
266-
using ModelingToolkit, OrdinaryDiffEq, Plots
267-
function UnitMassWithFriction(k; name)
268-
@variables t x(t)=0 v(t)=0
269-
D = Differential(t)
270-
eqs = [
271-
D(x) ~ v
272-
D(v) ~ sin(t) - k*sign(v) # f = ma, sinusoidal force acting on the mass, and Coulomb friction opposing the movement
273-
]
274-
ODESystem(eqs, t; continuous_events=[v ~ 0], name) # when v = 0 there is a discontinuity
275-
end
276-
@named m = UnitMassWithFriction(0.7)
277-
prob = ODEProblem(m, Pair[], (0, 10pi))
278-
sol = solve(prob, Tsit5())
279-
plot(sol)
280-
```
281-
282-
### Example: Bouncing ball
283-
In the documentation for DifferentialEquations, we have an example where a bouncing ball is simulated using callbacks which has an `affect!` on the state. We can model the same system using ModelingToolkit like this
284-
285-
```@example events
286-
@variables t x(t)=1 v(t)=0
287-
D = Differential(t)
288-
289-
root_eqs = [x ~ 0] # the event happens at the ground x(t) = 0
290-
affect = [v ~ -v] # the effect is that the velocity changes sign
291-
292-
@named ball = ODESystem([
293-
D(x) ~ v
294-
D(v) ~ -9.8
295-
], t; continuous_events = root_eqs => affect) # equation => affect
296-
297-
ball = structural_simplify(ball)
298-
299-
tspan = (0.0,5.0)
300-
prob = ODEProblem(ball, Pair[], tspan)
301-
sol = solve(prob,Tsit5())
302-
@assert 0 <= minimum(sol[x]) <= 1e-10 # the ball never went through the floor but got very close
303-
plot(sol)
304-
```
305-
306-
### Test bouncing ball in 2D with walls
307-
Multiple events? No problem! This example models a bouncing ball in 2D that is enclosed by two walls at $y = \pm 1.5$.
308-
```@example events
309-
@variables t x(t)=1 y(t)=0 vx(t)=0 vy(t)=2
310-
D = Differential(t)
311-
312-
continuous_events = [ # This time we have a vector of pairs
313-
[x ~ 0] => [vx ~ -vx]
314-
[y ~ -1.5, y ~ 1.5] => [vy ~ -vy]
315-
]
316-
317-
@named ball = ODESystem([
318-
D(x) ~ vx,
319-
D(y) ~ vy,
320-
D(vx) ~ -9.8-0.1vx, # gravity + some small air resistance
321-
D(vy) ~ -0.1vy,
322-
], t; continuous_events)
323-
324-
325-
ball = structural_simplify(ball)
326-
327-
tspan = (0.0,10.0)
328-
prob = ODEProblem(ball, Pair[], tspan)
329-
330-
sol = solve(prob,Tsit5())
331-
@assert 0 <= minimum(sol[x]) <= 1e-10 # the ball never went through the floor but got very close
332-
@assert minimum(sol[y]) > -1.5 # check wall conditions
333-
@assert maximum(sol[y]) < 1.5 # check wall conditions
334-
335-
tv = sort([LinRange(0, 10, 200); sol.t])
336-
plot(sol(tv)[y], sol(tv)[x], line_z=tv)
337-
vline!([-1.5, 1.5], l=(:black, 5), primary=false)
338-
hline!([0], l=(:black, 5), primary=false)
339-
```
340-
341-
### Generalized affect support
342-
In some instances, a more flexible response to events is needed, which cannot be encapsulated by
343-
an equation. For example, a component may implement complex behavior that it is inconvenient or
344-
impossible to capture in equations.
345-
346-
ModelingToolkit therefore supports Julia functions as affects: instead of an equation, an affect is
347-
defined as a `tuple`:
348-
```
349-
[x ~ 0] => (affect!, [v, x], [p, q], ctx)
350-
```
351-
352-
where, `affect!` is a Julia function with the signature: `affect!(integ, u, p, ctx)`; `[u,v]` and `[p,q]`
353-
are the states (variables) and parameters that are accessed by `affect!`, respectively; and `ctx` is a
354-
context that is passed to `affect!` as the `ctx` argument.
355-
356-
`affect!` receives the `DiffEqs` integrator as its first argument, which can then be used to access states
357-
and parameters that are provided in the `u` and `p` arguments (implemented as `NamedTuple`s):
358-
359-
```
360-
function affect!(integ, u, v, ctx)
361-
# integ.t is the current time
362-
# integ.u[u.v] is the value of the state `v` above
363-
# integ.p[p.q] is the value of the parameter `q` above
364-
end
365-
```
366-
367-
When accessing variables of a sub-system, it could be useful to rename them (alternatively, an affect function
368-
may be reused in different contexts):
369-
```
370-
[x ~ 0] => (affect!, [resistor₊v => :v, x], [p, q => :p2], ctx)
371-
```
372-
373-
Here, `resistor₊v` is passed as `v` while `q` has been renamed `p2`.
374-
375-
As an example, here is the bouncing ball example from `DiffEqs` using ModelingToolkit:
376-
377-
```@example events
378-
sts = @variables y(t), v(t)
379-
par = @parameters g = 9.8
380-
bb_eqs = [D(y) ~ v
381-
D(v) ~ -g]
382-
383-
function bb_affect!(integ, u, p, ctx)
384-
integ.u[u.v] = -integ.u[u.v]
385-
end
386-
387-
@named bb_model = ODESystem(bb_eqs, t, sts, par,
388-
continuous_events = [[y ~ 0] => (bb_affect!, [v], [], nothing)])
389-
390-
bb_sys = structural_simplify(bb_model)
391-
u0 = [v => 0.0, y => 50.0]
392-
393-
bb_prob = ODEProblem(bb_sys, u0, (0, 15.0))
394-
bb_sol = solve(bb_prob, Tsit5())
395-
396-
plot(bb_sol)
397-
```
398-
399-
### Discrete events support
400-
In addition to continuous events, discrete events are also supported.
401-
402-
TBD
403-
404-
Two important sub-classes of discrete events are periodic and set-time events. A periodic event is triggered at
405-
fixed intervals (e.g. every Δt seconds). To specify a periodic interval, pass the interval as the condition for
406-
the event:
407-
408-
```
409-
discrete_events=[1.0 => [v ~ -v]]
410-
```
411-
412-
will change the sign of `v` at t=1.0, 2.0, ...
413-
414-
Alternatively, the event may be triggered at specific set times:
415-
```
416-
discrete_events=[[1.0, 4.0] => [v ~ -v]]
417-
```
418-
419-
will change the sign of `v` *only* at t=1.0, 4.0.
420-
251+
When modeling, e.g., impacts, saturations or Coulomb friction, the dynamic
252+
equations are discontinuous in either the state or one of its derivatives. This
253+
causes the solver to take very small steps around the discontinuity, and
254+
sometimes leads to early stopping due to `dt <= dt_min`. The correct way to
255+
handle such dynamics is to tell the solver about the discontinuity by means of a
256+
root-finding equation, which can be modeling using [`ODESystem`](@ref)'s event
257+
support. Please see the tutorial on [Callbacks and Events](@ref events) for
258+
details and examples.

0 commit comments

Comments
 (0)