Skip to content

Commit 3f63145

Browse files
Merge pull request #1673 from dodoplus/func-affect
Func affect
2 parents 15a7d07 + df4cf9b commit 3f63145

File tree

12 files changed

+747
-61
lines changed

12 files changed

+747
-61
lines changed

docs/src/basics/Composition.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,3 +337,84 @@ plot(sol(tv)[y], sol(tv)[x], line_z=tv)
337337
vline!([-1.5, 1.5], l=(:black, 5), primary=false)
338338
hline!([0], l=(:black, 5), primary=false)
339339
```
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+

src/structural_transformation/codegen.jl

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using LinearAlgebra
22

3-
using ModelingToolkit: isdifferenceeq, has_continuous_events, generate_rootfinding_callback,
4-
generate_difference_cb, merge_cb
3+
using ModelingToolkit: isdifferenceeq, process_events
54

65
const MAX_INLINE_NLSOLVE_SIZE = 8
76

@@ -522,18 +521,11 @@ function ODAEProblem{iip}(sys,
522521
use_union)
523522

524523
has_difference = any(isdifferenceeq, eqs)
525-
if has_continuous_events(sys)
526-
event_cb = generate_rootfinding_callback(sys; kwargs...)
527-
else
528-
event_cb = nothing
529-
end
530-
difference_cb = has_difference ? generate_difference_cb(sys; kwargs...) : nothing
531-
cb = merge_cb(event_cb, difference_cb)
532-
cb = merge_cb(cb, callback)
524+
cbs = process_events(sys; callback, has_difference, kwargs...)
533525

534-
if cb === nothing
526+
if cbs === nothing
535527
ODEProblem{iip}(fun, u0, tspan, p; kwargs...)
536528
else
537-
ODEProblem{iip}(fun, u0, tspan, p; callback = cb, kwargs...)
529+
ODEProblem{iip}(fun, u0, tspan, p; callback = cbs, kwargs...)
538530
end
539531
end

src/systems/abstractsystem.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,6 +1348,7 @@ function Base.hash(sys::AbstractSystem, s::UInt)
13481348
end
13491349
s = foldr(hash, get_observed(sys), init = s)
13501350
s = foldr(hash, get_continuous_events(sys), init = s)
1351+
s = foldr(hash, get_discrete_events(sys), init = s)
13511352
s = hash(independent_variables(sys), s)
13521353
return s
13531354
end
@@ -1375,16 +1376,17 @@ function extend(sys::AbstractSystem, basesys::AbstractSystem; name::Symbol = nam
13751376
sts = union(get_states(basesys), get_states(sys))
13761377
ps = union(get_ps(basesys), get_ps(sys))
13771378
obs = union(get_observed(basesys), get_observed(sys))
1378-
evs = union(get_continuous_events(basesys), get_continuous_events(sys))
1379+
cevs = union(get_continuous_events(basesys), get_continuous_events(sys))
1380+
devs = union(get_discrete_events(basesys), get_discrete_events(sys))
13791381
defs = merge(get_defaults(basesys), get_defaults(sys)) # prefer `sys`
13801382
syss = union(get_systems(basesys), get_systems(sys))
13811383

13821384
if length(ivs) == 0
13831385
T(eqs, sts, ps, observed = obs, defaults = defs, name = name, systems = syss,
1384-
continuous_events = evs)
1386+
continuous_events = cevs, discrete_events = devs)
13851387
elseif length(ivs) == 1
13861388
T(eqs, ivs[1], sts, ps, observed = obs, defaults = defs, name = name,
1387-
systems = syss, continuous_events = evs)
1389+
systems = syss, continuous_events = cevs, discrete_events = devs)
13881390
end
13891391
end
13901392

0 commit comments

Comments
 (0)