Callback Function Example in DifferentialEquations.jl

I’m trying to understand how to use callback functions with Julia’s solve package. I’m using the code example pasted below and obtaining an error. I’m struggling to understand what I need to fix. Any ideas?

using DifferentialEquations

# Define your differential equation and initial condition
function f(dx, x, p, t)
    # Define your differential equation here
    dx[1] = -2.0 * x[1]  # Example: dx/dt = -2x
end
x0 = [1.0]  # Initial condition

# Time span
tspan = (0.0, 2.0)  # From t = 0 to t = 2

# Define the problem
prob = ODEProblem(f, x0, tspan)

# Create the solver
solver = Tsit5()  # Choose the solver (e.g., Tsit5, RK4, etc.)

# Define a callback function to print the solution at specific time points
function callback_func(sol, t, integrator)
    if t == 0.5 || t == 1.0
        println("Time: ", t, ", Solution: ", sol(t))
    end
    return false
end

# Solve the problem and get the output
sol = solve(prob, solver, callback=callback_func)
ERROR: MethodError: no method matching CallbackSet(::typeof(callback_func))
Closest candidates are:
  CallbackSet() at ~/.julia/packages/SciMLBase/BC0Kw/src/callbacks.jl:339
  CallbackSet(::SciMLBase.AbstractContinuousCallback) at ~/.julia/packages/SciMLBase/BC0Kw/src/callbacks.jl:338
  CallbackSet(::SciMLBase.AbstractDiscreteCallback) at ~/.julia/packages/SciMLBase/BC0Kw/src/callbacks.jl:337
  ...
Stacktrace:
 [1] __init(prob::ODEProblem{Vector{Float64}, Tuple{Float64, Float64}, true, SciMLBase.NullParameters, ODEFunction{true, typeof(f), LinearAlgebra.UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing}, Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, SciMLBase.StandardODEProblem}, alg::Tsit5{typeof(OrdinaryDiffEq.trivial_limiter!), typeof(OrdinaryDiffEq.trivial_limiter!), Static.False}, timeseries_init::Tuple{}, ts_init::Tuple{}, ks_init::Tuple{}, recompile::Type{Val{true}}; saveat::Tuple{}, tstops::Tuple{}, d_discontinuities::Tuple{}, save_idxs::Nothing, save_everystep::Bool, save_on::Bool, save_start::Bool, save_end::Nothing, callback::typeof(callback_func), dense::Bool, calck::Bool, dt::Float64, dtmin::Nothing, dtmax::Float64, force_dtmin::Bool, adaptive::Bool, gamma::Rational{Int64}, abstol::Nothing, reltol::Nothing, qmin::Rational{Int64}, qmax::Int64, qsteady_min::Int64, qsteady_max::Int64, beta1::Nothing, beta2::Nothing, qoldinit::Rational{Int64}, controller::Nothing, fullnormalize::Bool, failfactor::Int64, maxiters::Int64, internalnorm::typeof(DiffEqBase.ODE_DEFAULT_NORM), internalopnorm::typeof(LinearAlgebra.opnorm), isoutofdomain::typeof(DiffEqBase.ODE_DEFAULT_ISOUTOFDOMAIN), unstable_check::typeof(DiffEqBase.ODE_DEFAULT_UNSTABLE_CHECK), verbose::Bool, timeseries_errors::Bool, dense_errors::Bool, advance_to_tstop::Bool, stop_at_next_tstop::Bool, initialize_save::Bool, progress::Bool, progress_steps::Int64, progress_name::String, progress_message::typeof(DiffEqBase.ODE_DEFAULT_PROG_MESSAGE), userdata::Nothing, allow_extrapolation::Bool, initialize_integrator::Bool, alias_u0::Bool, alias_du0::Bool, initializealg::OrdinaryDiffEq.DefaultInit, kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ OrdinaryDiffEq ~/.julia/packages/OrdinaryDiffEq/eqC5v/src/solve.jl:208
 [2] __solve(::ODEProblem{Vector{Float64}, Tuple{Float64, Float64}, true, SciMLBase.NullParameters, ODEFunction{true, typeof(f), LinearAlgebra.UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing}, Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, SciMLBase.StandardODEProblem}, ::Tsit5{typeof(OrdinaryDiffEq.trivial_limiter!), typeof(OrdinaryDiffEq.trivial_limiter!), Static.False}; kwargs::Base.Pairs{Symbol, typeof(callback_func), Tuple{Symbol}, NamedTuple{(:callback,), Tuple{typeof(callback_func)}}})
   @ OrdinaryDiffEq ~/.julia/packages/OrdinaryDiffEq/eqC5v/src/solve.jl:4
 [3] solve_call(_prob::ODEProblem{Vector{Float64}, Tuple{Float64, Float64}, true, SciMLBase.NullParameters, ODEFunction{true, typeof(f), LinearAlgebra.UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing}, Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, SciMLBase.StandardODEProblem}, args::Tsit5{typeof(OrdinaryDiffEq.trivial_limiter!), typeof(OrdinaryDiffEq.trivial_limiter!), Static.False}; merge_callbacks::Bool, kwargshandle::KeywordArgError, kwargs::Base.Pairs{Symbol, typeof(callback_func), Tuple{Symbol}, NamedTuple{(:callback,), Tuple{typeof(callback_func)}}})
   @ DiffEqBase ~/.julia/packages/DiffEqBase/KouNZ/src/solve.jl:437
 [4] solve_up(prob::ODEProblem{Vector{Float64}, Tuple{Float64, Float64}, true, SciMLBase.NullParameters, ODEFunction{true, typeof(f), LinearAlgebra.UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing}, Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, SciMLBase.StandardODEProblem}, sensealg::Nothing, u0::Vector{Float64}, p::SciMLBase.NullParameters, args::Tsit5{typeof(OrdinaryDiffEq.trivial_limiter!), typeof(OrdinaryDiffEq.trivial_limiter!), Static.False}; kwargs::Base.Pairs{Symbol, typeof(callback_func), Tuple{Symbol}, NamedTuple{(:callback,), Tuple{typeof(callback_func)}}})
   @ DiffEqBase ~/.julia/packages/DiffEqBase/KouNZ/src/solve.jl:780
 [5] solve(prob::ODEProblem{Vector{Float64}, Tuple{Float64, Float64}, true, SciMLBase.NullParameters, ODEFunction{true, typeof(f), LinearAlgebra.UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing}, Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, SciMLBase.StandardODEProblem}, args::Tsit5{typeof(OrdinaryDiffEq.trivial_limiter!), typeof(OrdinaryDiffEq.trivial_limiter!), Static.False}; sensealg::Nothing, u0::Nothing, p::Nothing, kwargs::Base.Pairs{Symbol, typeof(callback_func), Tuple{Symbol}, NamedTuple{(:callback,), Tuple{typeof(callback_func)}}})
   @ DiffEqBase ~/.julia/packages/DiffEqBase/KouNZ/src/solve.jl:760
 [6] top-level scope
   @ REPL[8]:2

There is no such thing as just using a function like that. Did you mean to define a DiscreteCallback? See the documentation:

https://docs.sciml.ai/DiffEqDocs/stable/features/callback_functions/

Yes – I think I did, I see there’s a code example too here. thanks!