# Unexpected behaviour from Continuous Callback in DifferentialEquations.jl simulating Lotka-Volterra Dynamics

**URL:** <https://discourse.julialang.org/t/unexpected-behaviour-from-continuous-callback-in-differentialequations-jl-simulating-lotka-volterra-dynamics/69404>\
**Category:** General Usage\
**Tags:** question, diffeq\
**Created:** [October 8, 2021, 12:17am UTC](https://discourse.julialang.org/t/unexpected-behaviour-from-continuous-callback-in-differentialequations-jl-simulating-lotka-volterra-dynamics/69404 "2021-10-08T00:17:55Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![colebrookson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/colebrookson/32/19567_2.png) [@colebrookson](https://discourse.julialang.org/u/colebrookson)\
**Post date:** [October 8, 2021, 12:17am UTC](https://discourse.julialang.org/t/unexpected-behaviour-from-continuous-callback-in-differentialequations-jl-simulating-lotka-volterra-dynamics/69404/1 "2021-10-08T00:17:55Z")

</div>

Hi there,

So I’m using a `ContinuousCallback` method to simulate a simple Lotka-Volterra consumer-resource system of equations:

\frac{dC}{dt} = (\alpha - \beta R)C

\frac{dR}{dt} = (-\gamma + \delta C)R

My goal is to simply create a callback such that when the resource R goes below a certain value (in my example, 60), the callback function takes the integrator to zero.

Using the [bouncing ball example](https://diffeq.sciml.ai/stable/features/callback_functions/#ContinuousCallback-Examples) as a basis, I have formatted the code as following:

```julia
function dynamics!(du,u,p,t)
  α, β, γ, δ = p
  du[1] = (α - β * u[2]) * u[1]
  du[2] = (-γ + δ * u[1]) * u[2]
end

function zero_condition!(u,t,integrator) # Event when event_f(u,t) <= 60
  u[1] <= 60
end

function zero_affect!(integrator)
  integrator.u[1] = 0
end

zero_event = ContinuousCallback(zero_condition!,zero_affect!)

u0 = [100, 100]
p = [0.55, 0.028, 0.84, 0.026]
tspan = (0.0,50.0)
prob = ODEProblem{true}(dynamics!,u0,tspan,p)
sol = solve(prob,Tsit5(), callback = zero_event)
plot(sol)

```

The code runs, and produces the following solution:

```julia
print(sol)
u: [[100.0, 100.0], [80.80845686345101, 114.00221332347269], [59.438166455464156, 126.7071869446583], [38.37959894566831, 134.27941573269763], [18.843731217381873, 130.52474821796022], [11.656653966686788, 121.35951343513864], [6.436587909054811, 105.673103443787], [3.874110828007275, 89.5763973309722], [2.2403782888041226, 69.74988819115794], [1.4937160732283663, 52.82058943966623], [1.1299759092760473, 38.650593036020666], [0.9615473179089438, 26.694047651872364], [0.9337419507717553, 17.161970095532308], [1.054390144771701, 9.912042531836985], [1.347009098584734, 5.531988127100285], [1.8952712402506138, 2.955913455350605], [2.8618179586434507, 1.5337792854669305], [4.579730793457142, 0.7807746687626642], [7.765207862902192, 0.3974028644034909], [14.716051089510543, 0.20591489694526696], [26.934456181374337, 0.14530645535833772], [51.10710701790373, 0.17148599156666613], [97.11195013965043, 0.5789527461182298], [0.0, 0.5789527461182298], [0.0, 0.2140524219697758], [0.0, 0.07916734689187103], [0.0, 0.030792621980557648], [0.0, 0.012118725928445456], [0.0, 0.004872909916774048], [0.0, 0.0019705425705026595], [0.0, 0.0013744561046710527]]

```

![Screenshot from 2021-10-07 20-12-24](https://global.discourse-cdn.com/julialang/original/3X/8/5/850b1a8e37db0c410aaf6e64c837f006acc85ec2.png)

Where I am confused is that the callback is doing something, I can see it take the value of `u[1]` to zero, but it’s not at the value 60.

I’m sure this is a fundamental misunderstanding on my part of how to handle the `ContinuousCallback`, but I can’t find an answer to this in the docs, and would appreciate any help!

---

<div class="post-metadata">

**Author:** ![tomaklutfu](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomaklutfu/32/2411_2.png) [@tomaklutfu](https://discourse.julialang.org/u/tomaklutfu)\
**Post date:** [October 8, 2021, 5:32am UTC](https://discourse.julialang.org/t/unexpected-behaviour-from-continuous-callback-in-differentialequations-jl-simulating-lotka-volterra-dynamics/69404/2 "2021-10-08T05:32:58Z")

</div>

> [@colebrookson](#):
>
> ```julia
> function zero_condition!(u,t,integrator) # Event when event_f(u,t) <= 60
> u[1] <= 60
> end
> 
> ```

You need a zero crossing function in continuous callback. It is triggered by sign change(-1 to 1, up-crossing or 1 to -1, down-crossing) of the return value. Try below function

```julia
function zero_condition!(u,t,integrator) # Event when event_f(u,t) <= 60
  60-u[1] #Detection for down-crossing change
end

```

Note: Sorry, I made a mistake in early version. Updated the function as suggested in the next comment.

---

<div class="post-metadata">

**Author:** ![hzgzh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hzgzh/32/6596_2.png) [@hzgzh](https://discourse.julialang.org/u/hzgzh)\
**Post date:** [October 8, 2021, 8:47am UTC](https://discourse.julialang.org/t/unexpected-behaviour-from-continuous-callback-in-differentialequations-jl-simulating-lotka-volterra-dynamics/69404/3 "2021-10-08T08:47:59Z")

</div>

try

```julia
function zero_condition!(u,t,integrator) # Event when event_f(u,t) <= 60
  u[1] - 60
end

zero_event = ContinuousCallback(zero_condition!,zero_affect!, nothing)

```

affect! is upcross, affect\_neg! is downcross, so need set affect\_neg! to nothing

---

<div class="post-metadata">

**Author:** ![colebrookson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/colebrookson/32/19567_2.png) [@colebrookson](https://discourse.julialang.org/u/colebrookson)\
**Post date:** [October 17, 2021, 12:20am UTC](https://discourse.julialang.org/t/unexpected-behaviour-from-continuous-callback-in-differentialequations-jl-simulating-lotka-volterra-dynamics/69404/5 "2021-10-17T00:20:47Z")

</div>

Follow-up question:

If I wanted to check if both `u[1]` and `u[2]` go below 60 what would be the best way to do that, specifically that would scale if there were a large number of equations (i.e. `u` had 50 elements)?

I can do it this explicit way, wherein I simply write it out twice:

```julia
function dynamics!(du,u,p,t)
  α, β, γ, δ = p
  du[1] = (α - β * u[2]) * u[1]
  du[2] = (-γ + δ * u[1]) * u[2]
end

function u1_zero_condition!(u,t,integrator) # Event when event_f(u,t) == 0
  60-u[1]
end
function u2_zero_condition!(u,t,integrator) # Event when event_f(u,t) == 0
  60-u[2]
end

function u1_zero_affect!(integrator)
  integrator.u[1] = 0
end
function u2_zero_affect!(integrator)
  integrator.u[2] = 0
end

u1_zero_event = ContinuousCallback(u1_zero_condition!,u1_zero_affect!)
u2_zero_event = ContinuousCallback(u2_zero_condition!,u2_zero_affect!)

cb_set = CallbackSet(u1_zero_event, u2_zero_event)

```

With the same run section:

```julia
u0 = [100, 100]
p = [0.55, 0.028, 0.84, 0.026]
tspan = (0.0,20.0)
prob = ODEProblem{true}(dynamics!,u0,tspan,p)
sol = solve(prob,Tsit5(), callback = cb_set)
plot(sol)
@show sol[end]
print(sol)
minimum(sol)

```

Which produces this:  
 ![Screenshot from 2021-10-16 20-16-46](https://global.discourse-cdn.com/julialang/original/3X/7/9/798852b3378ae5bab88aceb0197dd7aace0e022e.png)  
So I know I’m getting exactly what I want. However, writing it out manually for a large system (i.e. 50) seems inefficient. Is there a standard way of doing this? Or alternatively, could I somehow have one `condition!` and one `affect!` function check all of `u[i]` where there are `i=50` elements?

Any thoughts much appreciated!

---

<div class="post-metadata">

**Author:** ![tomaklutfu](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomaklutfu/32/2411_2.png) [@tomaklutfu](https://discourse.julialang.org/u/tomaklutfu)\
**Post date:** [October 17, 2021, 6:33am UTC](https://discourse.julialang.org/t/unexpected-behaviour-from-continuous-callback-in-differentialequations-jl-simulating-lotka-volterra-dynamics/69404/6 "2021-10-17T06:33:35Z")

</div>

You can use `VectorContinuousCallback`. The docs are [here](https://diffeq.sciml.ai/stable/features/callback_functions/#VectorContinuousCallback)

---

<div class="post-metadata">

**Author:** ![colebrookson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/colebrookson/32/19567_2.png) [@colebrookson](https://discourse.julialang.org/u/colebrookson)\
**Post date:** [October 19, 2021, 1:17am UTC](https://discourse.julialang.org/t/unexpected-behaviour-from-continuous-callback-in-differentialequations-jl-simulating-lotka-volterra-dynamics/69404/7 "2021-10-19T01:17:42Z")

</div>

Sorry to come back on this, using `VectorContinuousCallback` but couldn’t find any examples of having multiple forms of callbacks, wonder if you have insight on this. So I’m trying to perform the above task (taking the function to zero if it crosses some value), but ALSO, change the parameter values at some specific times. I could previously achieve that with `DiscreteCallbacks` but now I’m trying to get it to play nice with the vector of callbacks.

I tried to implement both through the `out[i]` component of the `condition`, but could only get it to do either taking the function to zero OR change the parameters, but not both.

Code here:

```julia
function dynamics!(du,u,p,t)
  α, β, γ, δ = p
  du[1] = (α - β * u[2]) * u[1]
  du[2] = (-γ + δ * u[1]) * u[2]
end

# set constant stop values
const stop1 = [1.0]
const stop2 = [3.0]

function condition(out,u,t,integrator)
  out[1] = t in stop1
  out[2] = t in stop2
  out[3] = 20-u[1]
  out[4] = 20-u[2]
end

function affect!(integrator, idx)
  if idx == 1
    integrator.p[4] += 4
  end
  if idx == 2
    integrator.p[4] -= 4
  end
  if idx == 3
    integrator.u[1] = 0
  end
  if idx == 4
    integrator.u[2] = 0
  end
end

cb = VectorContinuousCallback(condition,affect!,4,save_positions=(true,true))

u0 = [100, 100]
p = [0.55, 0.028, 0.84, 0.026]
tspan = (0.0,20.0)
const tstop = [1.;3.]
prob = ODEProblem{true}(dynamics!,u0,tspan,p)
sol = solve(prob,Tsit5(), callback = cb, tstops=tstop)
plot(sol)

```

which gives me this:  
 ![Screenshot from 2021-10-18 21-14-50](https://global.discourse-cdn.com/julialang/original/3X/b/b/bbbb82504936f58749b444d28afaf248818fa677.png)

However, if i simply remove the second two indices of `out[i]` like this,

```julia
function condition(out,u,t,integrator)
  out[1] = t in stop1
  out[2] = t in stop2
end

```

but change nothing else, I see the parameter change take effect, and it looks like this:

![Screenshot from 2021-10-18 21-15-52](https://global.discourse-cdn.com/julialang/original/3X/6/8/68e1757f72b303071906e8fb89c6b0e25f49601e.png)

How can I get BOTH of these callbacks to work together in the callback set?

Again, any help much appreciated and thanks for all your help so far!

---

<div class="post-metadata">

**Author:** ![tomaklutfu](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomaklutfu/32/2411_2.png) [@tomaklutfu](https://discourse.julialang.org/u/tomaklutfu)\
**Post date:** [October 19, 2021, 3:32am UTC](https://discourse.julialang.org/t/unexpected-behaviour-from-continuous-callback-in-differentialequations-jl-simulating-lotka-volterra-dynamics/69404/8 "2021-10-19T03:32:04Z")

</div>

Your first two condition is boolean(true, false). They also should be sign changingin values, afaik. Something like

```julia
out[1] = 1.0 - t
out[3] = 3.0 - t

```

Instead, you can combine these 2 to a `DiscreteCallback`, while its `condition` checks and returns true in case of any discrete event and `affect` reacts appropriate action depending on time. In this case, you need to add `tstops` to integrator while making them into `VectorContinuousCallback` catches regardless of `tstops`.
