I’m trying to use a VectorContinuousCallback
to handle friction and end stops. Some of the conditions are not based on the state variables u
but on a value stuffed into a field of the parameters p
.
Code:
using DifferentialEquations
using Plots
using Parameters
using StaticArrays
@enum State LEFT_STOP SLIDING_RIGHT STOPPED SLIDING_LEFT RIGHT_STOP
@with_kw mutable struct Params
M::Float64 = 1.0 # mass of sliding object in kg
ga::Float64 = 9.81 # gravitational constant, m/s^2
μs::Float64 = 0.8 # coefficient of static friction
μk::Float64 = 0.5 # coefficient of dynammic friction
cor::Float64 = 0.2 # coefficient of restitution, 0=inelastic, 1=elastic
xls::Float64 = -0.5 # location of left stop in m
xrs::Float64 = 0.5 # location of right stop in m
state::State = STOPPED # state of system
netforce::Float64 = 0.0 # to be written by the ODE so callbacks can see force
end
function FricSMCondx(out, u, t, integ)
@unpack_Params integ.p
v,x = u
out[1] = (state==LEFT_STOP)*netforce
out[2] = (state==SLIDING_RIGHT)*(-v)
out[3] = (state==SLIDING_RIGHT)*(x-xrs)
out[4] = (state==STOPPED)*(netforce)
out[5] = (state==STOPPED)*(-netforce)
out[6] = (state==SLIDING_LEFT)*v
out[7] = (state==SLIDING_LEFT)*(-(x-xls))
out[8] = (state==RIGHT_STOP)*(-netforce)
println("condx ",t," ",out)
end
function FricSMaffect!(integ, idx)
println("affect ",idx)
if idx==1 integ.p.state = SLIDING_RIGHT
elseif idx==2 integ.p.state = STOPPED
elseif idx==3 integ.p.state = RIGHT_STOP
elseif idx==4 integ.p.state = SLIDING_RIGHT
elseif idx==5 integ.p.state = SLIDING_LEFT
elseif idx==6 integ.p.state = STOPPED
elseif idx==7 integ.p.state = LEFT_STOP
elseif idx==8 integ.p.state = SLIDING_LEFT
end
end
cb = VectorContinuousCallback(FricSMCondx, FricSMaffect!, nothing, 8)
force_ext(t) = 1.0*t
function sbode(u,p,t)
v,x = u # unpacking state variables
@unpack_Params p # unpack parameters by name into function scope
if state==LEFT_STOP
p.netforce = force_ext(t)-M*ga*μs # friction to the left
dv = 0.0 # not moving
dx = 0.0 # not moving
elseif state==SLIDING_RIGHT
p.netforce = force_ext(t)-M*ga*μk # friction to the left
dv = M*p.netforce
dx = v
elseif state==STOPPED
fext = force_ext(t)
p.netforce = fext-M*ga*μs*sign(fext)
dv = 0.0
dx = 0.0
elseif state==SLIDING_LEFT
p.netforce = force_ext()+M*ga*μk # friction to the right
dv = M*p.netforce
dx = v
else # state==RIGHT_STOP
Ffriction = M*ga*μs # positive because to the right
p.netforce = force_ext(t)+Ffriction
dv = 0.0
dx = 0.0
end
println("ode ",t," ",netforce, " ",state)
@SVector[dv, dx]
end
function sim()
u0 = @SVector[0.0, 0.0]
tspan = (0.0, 100.0)
p = Params()
prob = ODEProblem(sbode, u0, tspan, p)
sol = solve(prob, callback = cb)
end
sol = sim()
The idea is that when the system is stopped due to friction, the callback will trigger on the force becoming high enough to cause motion, then the state will be changed, changing the model behavior accordingly.
However, what I see from an excerpt of the println
outputs is the following:
ode 0.2720999999999999 -7.736900000000001 STOPPED
ode 0.4380999999999998 -7.575900000000001 STOPPED
ode 1.0110999999999997 -7.409900000000001 STOPPED
ode 1.091125540904509 -6.836900000000001 STOPPED
ode 1.1110999999999995 -6.756874459095492 STOPPED
ode 1.1110999999999995 -6.736900000000001 STOPPED
condx 0.11109999999999996 [-0.0, -0.0, -0.0, -6.736900000000001, 6.736900000000001, 0.0, -0.0, 0.0]
condx 1.1110999999999995 [-0.0, -0.0, -0.0, -6.736900000000001, 6.736900000000001, 0.0, -0.0, 0.0]
condx 0.22221111111111103 [-0.0, -0.0, -0.0, -6.736900000000001, 6.736900000000001, 0.0, -0.0, 0.0]
condx 0.3333222222222221 [-0.0, -0.0, -0.0, -6.736900000000001, 6.736900000000001, 0.0, -0.0, 0.0]
condx 0.4444333333333331 [-0.0, -0.0, -0.0, -6.736900000000001, 6.736900000000001, 0.0, -0.0, 0.0]
condx 0.5555444444444442 [-0.0, -0.0, -0.0, -6.736900000000001, 6.736900000000001, 0.0, -0.0, 0.0]
condx 0.6666555555555553 [-0.0, -0.0, -0.0, -6.736900000000001, 6.736900000000001, 0.0, -0.0, 0.0]
condx 0.7777666666666664 [-0.0, -0.0, -0.0, -6.736900000000001, 6.736900000000001, 0.0, -0.0, 0.0]
condx 0.8888777777777774 [-0.0, -0.0, -0.0, -6.736900000000001, 6.736900000000001, 0.0, -0.0, 0.0]
condx 0.9999888888888885 [-0.0, -0.0, -0.0, -6.736900000000001, 6.736900000000001, 0.0, -0.0, 0.0]
condx 1.1110999999999995 [-0.0, -0.0, -0.0, -6.736900000000001, 6.736900000000001, 0.0, -0.0, 0.0]
ode 2.7210999999999985 -6.736900000000001 STOPPED
ode 4.381099999999998 -5.126900000000003 STOPPED
ode 10.111099999999995 -3.4669000000000025 STOPPED
ode 10.911355409045091 2.2630999999999943 STOPPED
ode 11.111099999999993 3.0633554090450907 STOPPED
ode 11.111099999999993 3.2630999999999926 STOPPED
condx 1.1110999999999995 [0.0, -0.0, -0.0, 3.2630999999999926, -3.2630999999999926, 0.0, -0.0, -0.0]
condx 11.111099999999993 [0.0, -0.0, -0.0, 3.2630999999999926, -3.2630999999999926, 0.0, -0.0, -0.0]
condx 2.22221111111111 [0.0, -0.0, -0.0, 3.2630999999999926, -3.2630999999999926, 0.0, -0.0, -0.0]
condx 3.3333222222222205 [0.0, -0.0, -0.0, 3.2630999999999926, -3.2630999999999926, 0.0, -0.0, -0.0]
condx 4.444433333333331 [0.0, -0.0, -0.0, 3.2630999999999926, -3.2630999999999926, 0.0, -0.0, -0.0]
condx 5.5555444444444415 [0.0, -0.0, -0.0, 3.2630999999999926, -3.2630999999999926, 0.0, -0.0, -0.0]
condx 6.666655555555551 [0.0, -0.0, -0.0, 3.2630999999999926, -3.2630999999999926, 0.0, -0.0, -0.0]
condx 7.777766666666662 [0.0, -0.0, -0.0, 3.2630999999999926, -3.2630999999999926, 0.0, -0.0, -0.0]
condx 8.888877777777772 [0.0, -0.0, -0.0, 3.2630999999999926, -3.2630999999999926, 0.0, -0.0, -0.0]
condx 9.999988888888883 [0.0, -0.0, -0.0, 3.2630999999999926, -3.2630999999999926, 0.0, -0.0, -0.0]
condx 11.111099999999993 [0.0, -0.0, -0.0, 3.2630999999999926, -3.2630999999999926, 0.0, -0.0, -0.0]
In this test case, out[4]
of the conditional crosses zero in a rising direction between 1.11 s and 11.1 s (see the final iterations of the ode at those times) but when the conditional is tested at different time points it has fixed values of the p.netforce
field and the affect function is never fired. Is there a way to structure this so the conditional can see (and interpolate on to find the zero cross) the parameter value?