Problem
I’m trying to change the parameter of a dynamical system gradually.
In the documentation for the DifferentialEquations
library there is an emphasis on abrupt / instantaneous change - when explaining callbacks ; i.e. something hits a wall; gets kicked, fast drug dosage kinetics…
However the systems I’m trying to model show gradual changes in parameters (mostly linear with time) at discrete timepoints.
While I could achieve such change using a ContinuousCallback
, I’m suspecting that I should use the VectorContinuousCallback
instead - however it has sadly no example yet.
Or maybe even use the DiffEqCallbacks.jl
library instead?
Reproducable Example:
Gradual breaking of an harmonic oscillator, i.e. the damping force \omega gets linearly increased by 0.5 twice, at t = 25 and t = 75 each over a duration of 10 seconds.
using OrdinaryDiffEq, Plots, ModelingToolkit
# Example dynamical system:
harmonicoscillator(ddu,du,u,ω,t) = ddu .= -ω^2 * u
# Initial Conditions, timescale, stepsize
x₀ = [0.0]; dx₀ = [π/2]; ω = .75
tspan = (0.0, 100.0); dt = 0.001
# Im using ModelingToolkit for my models mostly
@named sys = modelingtoolkitize(
SecondOrderODEProblem(harmonicoscillator, dx₀, x₀, tspan, ω)
)
prob = ODEProblem(complete(sys))
as previous mentioned I am looking to increasing the force ω gradually over 10 (seconds) twice;
once at t=25 and once at t=75
timeranges = [25.0:dt:35.0, 75.0:dt:85.0] # should i collect the vector here?
Δω = 0.5 # absolute change of force ω
duration = length(timeranges[1]) # duration in timesteps of the acceleration
# This is the expected force after two changes:
expected_force = ω + ( length(timeranges) * Δω ) # i.e. : 0.75 + (2*0.5) = 1.75
# This is the naive callback i coded:
# check if the current timestep falls in one of the timeranges:
condition(u, t, integrator) = !any(t .∈ timeranges) |> Int # hit if: == zero
# [NOTE] modelingtoolkitize assigns symbols for parameters ie force ω is now α :
affect!(integrator) = integrator.ps[:α] += Δω / duration # or (Δω * dt) / 10
# how would i handle different durations here for example ?!
cb = ContinuousCallback(condition, affect!)
solving the system:
for the tstops
argument collect the values of the ranges, vector them, splat and concatenate, I feel this is not how its supposed to go?!
sol = solve(prob, callback = cb, dt = dt, tstops = vcat(collect.(timeranges)...) )
plot(sol , idxs = 2); vline!(vcat(collect.(extrema.(timeranges))...))
prob.ps[:α] ≈ expected_force # true; 1.750000000000166
Question
How bad is this? how to use VectorContinuousCallback
or DiffEqCallbacks.jl
to achieve the same behaviour (ideally with cleaner code, and better performance).
If this is already answered, please feel free to just point me to the resource and close this problem.
Thanks!