Accessing solutions from earlier times in Callback functions

Hi everyone, I’ve only recently started using Julia and I was wondering if it’s possible to access the values obtained at earlier times by a solver in a callback function.

For example, I’m trying to identify maxima (as a function of time) in the solution of a simple oscillator and store them in a vector, but I don’t know how/if it’s possible to access the values of u at previous times to compare them to the value at the current time. I’ve written out the logic I’m trying to achieve in the code below, but I don’t know how to define u_prev or u_prev2 in the condition function. I want for the affect! function to store the current time when u_prev is a local maximum.

using DifferentialEquations, Plots

function condition(u, t, integrator)
	# u_prev = ?    # Value of u[1] at the previous time
	# u_prev2 = ?   # Value of u[1] at the time before the previous time
	u_curr = u[1]
	if (u_curr < u_prev) && (u_prev2 < u_prev)
		return 0
	else
		return 1
	end
end
function affect!(integrator)
	push!(peaks, integrator.t)   # Store current time in peaks vector
end
cb = ContinuousCallback(condition, affect!)

# Initialise problem
u0 = ones(2)
p = (2.0, 1.0)
peaks = zeros(0)
function f!(du,u,p,t)
	a, b = p
	du[1] = a * u[2]
	du[2] = -b * u[1]
end
prob = ODEProblem(f!, u0, (0.0, 50.0), p)
sol = solve(prob, Tsit5(), maxiters = 1e3, callback = cb, abstol=1e-6, reltol=1e-5)

I was thinking that I might be able to access previous values of u through the integrator, but I haven’t been able to figure this out. Similarly, when I try to display the value of integrator.du, only the word “nothing” is outputted.

Any help would be greatly appreciated, and thank you in advance for your help!

1 Like

Since u'(t) = 0 and u''(t) < 0 at the maxima, you can write a continuous callback which evaluates those terms, and saves the current state at time t, I think? Your condition instead seems to be trying to find maxima discretely from u_{n-1}, u_n, and u_{n+1}, which is (afaik!) not something the callback interface can do. If you really want to do that, then I would not run it online (with the ODE solve) but analyze the solution array after it is complete.

I think that a continuous callback function that calculates when u_1'(t)=0 and u_1''(t)<0 would be ideal, but I haven’t been able to find out how to evaluate the derivatives of u(t) within a callback function. I don’t know how/if it’s possible to explicitly pass the derivatives to the callback function, and I don’t think they’re accessible through the integrator.

Do you know how I can access u_1'(t) and u_1''(t) within the callback function?

integrator(t,Val{i}) for the ith derivative.

Thanks for the help!

After testing, I’ve found that I can store the times of maxima in u[1] using the functions:

condition(u, t, integrator) = integrator(t, Val{1})[1]
function affect!(integrator)
	if (integrator(integrator.t, Val{2})[1] < 0.0)
		push!(peaks, integrator.t)   # Store current time in peaks vector
	end
end
cb = ContinuousCallback(condition, affect!)

I’ve used the time stored in the integrator (integrator.t) in the affect! function since t is not passed to it explicitly, but do you know exactly what time this will correspond to? Ideally I would want the time at which the condition is satisfied, but I wanted to check whether integrator.t would reference the time at the end of the current interval in this case (as I would expect it to if I displayed it from the condition function).

Also, when using these functions in my original code two values of time are outputted to the peaks vector for each maxima in u[1]. These times are very similar (for example 0.6755106670700617 and 0.6755108454503309 for the first maxima). Is this due to floating point errors in the observed derivatives over short time intervals?