DifferentialEquations.jl
DifferentialEquations.jl copied to clipboard
`PeriodicCallback` not triggering with `Rational` numbers
It can be nice to use Rational
numbers in PeriodicCallback
s if you have a bunch of different discrete things that fire at different rates. That way you can take the gcd
of the sample Δt
s, have one single callback function that checks if the current t
is a multiple of the specific Δt
for each affect. The problem is it seems that Rational
numbers don't cause the PeriodicCallback
to trigger.
using DifferentialEquations
function continuous_dynamics!(du, u, p, t)
(; f, A, B) = p
du .= A*u + B*f[]
end
function control_update!(integrator)
(; p, u) = integrator
integrator.p.f[] = -p.k'u
end
p = (
k = [8, 4],
f = Ref(0.0),
A = [0 1; 2 0],
B = [0, 1],
)
x0 = [1.0, 0.0]
tspan = (0.0, 2.0)
control_Δt = 1//10
prob = ODEProblem(continuous_dynamics!, x0, tspan, p)
cb = PeriodicCallback(control_update!, control_Δt; initial_affect=true)
sol = solve(prob, OrdinaryDiffEq.Tsit5(), callback=cb)
Plotting this we see that the control never triggers past t=0
:
If we change control_Δt
to a Float64
, though, it works as expected:
I currently work around this by just converting it to Float64
for the callback and then converting back to the Rational{Int}
that's the closest multiple of the overall Δt
. Which works, it's just a little hacky.
That's because for example there is no Float64 which equals 3//10 so Float64(3//10) == 3//10 == false
. We could change https://github.com/SciML/DiffEqCallbacks.jl/blob/master/src/iterative_and_periodic.jl#L101 the condition to be, instead of exact, make it within some epsilon tolerance to account for rounding.