-
-
Notifications
You must be signed in to change notification settings - Fork 236
Periodic Callbacks with general affect function (PoC) #1649
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
test/periodiccb.jl
Outdated
|
|
||
| period = 1.0 | ||
|
|
||
| @variables t |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are defined in the example folder. You can just include the file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a more simplified example/test.
The affect! function performs an update based in the state of the controller, which can perform a complex logic (here simulated by pseudo random number generator) as well as keep (and use) a history. This can be useful, e.g. when simulating hysteresis.
using ModelingToolkit, OrdinaryDiffEq, Test
using Random
struct MyController
rng
h
MyController() = new(MersenneTwister(1234), Float32[])
end
function newdirection!(c::MyController, pars, t)
# random update
pars.a, = Random.rand(c.rng, 1)
# use history for update
pars.b = t + sum(c.h)
end
function updatehist!(c::MyController, sts)
push!(c.h, sts.x)
end
function affect!(u, p, t, ctrl::MyController)
updatehist!(ctrl, u)
newdirection!(ctrl, p, t)
end
period = 1.0
@variables t, x(t)
@parameters a, b
D = Differential(t)
eqs = [ D(x) ~ a * x + b]
controller = MyController()
@named model = ODESystem(eqs, t, [x], [a, b], periodic_events=(1.0, affect!, controller))
sys = structural_simplify(model)
prob = ODAEProblem(sys, [x => 1.0], (0, 2.0), [a => 1.0, b => 3.0])
sol = solve(prob, Tsit5())
@test length(controller.h) == 1
@test controller.h[1] ≈ 7.873127
YingboMa
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure about this design. Cannot we detect periodic callbacks via discrete operators?
|
That would only allow update equations, not arbitrary |
Absolutely. The point is less about periodic (and discrete in general) events, and more about a more general-purpose I've tried to define a reasonable (I hope) interface for such |
|
|
||
| # find indexes | ||
| ind(sym, v) = findfirst(isequal(sym), v) | ||
| inds(syms, v) = filter(!isnothing,map(sym -> ind(sym, v), syms)) # filter out eliminated symbols |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To have better scaling, you can use Dict(reverse(en) for en in enumerate(all_dvs))
|
|
||
| # helper for affect | ||
| struct VarDict | ||
| ₊v::Base.RefValue{Vector} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need Ref? You can always resize! and copyto!.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, should it be just Vector{Int}?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need
Ref? You can alwaysresize!andcopyto!.
Not sure. I assume that each affect! only needs to access a small part of the overall variable array, so it may be cheaper to use references to those instead of copying the whole thing.
YingboMa
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Each callback requires at least one dictionary look up. Can we just generate affect functions such that it only does integer indexing? The symbol => index mapping is fixed after the model is compiled.
I'm not sure what you mean by that: function setvec!(vd::VarDict, v)
vd.₊v[] = v
endWithin the function Base.getproperty(vd::VarDict, name::Symbol)
...
₊v[][₊d[name]]
endAs you say, the mapping is fixed (and is only computed once), so we could compile the |
|
|
Is this a problem in this case, though? I think it would be quite rare for a single component to have even tens of parameters/states.
I'm not sure what you mean by that. The dictionary in the original proposal was only calculated once (as part of the |
|
@ChrisRackauckas @YingboMa I've switched back to an improved |
|
will be replaced with a new pull request |
This is a proof-of-concept of running general Julia code in event callbacks (in this case, periodic callbacks which are now supported alongside continuous callbacks).
Using equations to define an
affect!is great when it works, but it can be limiting.Defining a new event