You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Simply put, I am trying to implement a saturation feature on ODEs' output by detecting a threshhold value via a continuous callback.
Then via a discrete callback I can check if yes or no I can deactivate the saturation. The problem is that once the continuous callback triggers and the problem is modified (i.e. activate the saturation by flipping a boolean state) two strange behaviours happen :
If the discrete callback is activated, the integrator outputs erroneous results different from what the expected output of the ODE should be
If the discrete callback is deactivated (it was done in order to get a hint at the problem's origin), I realize that an error is thrown and the solver seems to struggle to handle the discontinuity.
Expected behavior
If the discrete callback is to be activated, I would expect from the integrator correct results. Because it is easy in this example to know how the solution should evolve (detailed in Error and Stacktrace section).
If the discrete callback is to be deactivated, I would expect the discontinuity to be handled when it is caused in an affect function of the continuous callback. However an error is thrown when the problem is modified.
Minimal Reproducible Example 👇
using DifferentialEquations
using Plots
#params
H =0.0371
ΔPe =0.05
saturated = [false]
observer = [false]
p = (H, ΔPe, saturated)
#DAE definitionfunctionsystem!(du, u, p, t)
H, ΔPe, saturated = p
du[1] =-5.0u[1] -1.0u[5]
du[2] =0.0228532u[1] -0.0263158u[2]
du[3] =0.789474u[1] +6.0u[2] -2u[3]
du[4] = (-0.263158u[1] -2.0u[2] +1.0u[3] -1.0u[4] ) *!saturated[1]
du[5] = (u[4] -ΔPe)/(2*H)
end#Continuous Callbackfunctioncondition(u, t, integrator)
u[4] -0.06endfunctionaffect!(integrator)
observer[1] =true
integrator.p[3] .=true
integrator.u[4] =0.06end
cb1 =ContinuousCallback(condition, affect!, nothing);
#Discrete Callbackfunctioncondition_(u, t, integrator)
Bool(observer[1])
endfunctionaffect_!(integrator)#Check if the value is increasing without any limitation; if yes, continue to saturate
integrator.p[3] .=false
s_t = integrator.sol(integrator.t)
s_tplus = integrator.sol(integrator.t+integrator.dt)#evaluate next value without saturation@show s_t[4], s_tplus[4]
#if s_tplus[4] <0.06#if under limit, deactivate saturation
observer[1] =falseelseif s_tplus[4] >=0.06#if still above limit, maintain saturation
integrator.p[3] .=trueendend
cb2 =DiscreteCallback(condition_, affect_!)
cb =CallbackSet(cb1, cb2);
#DAE Solving
M = [1.0000001.0000001.0000000000001.0]
u0 = [0.0, 0.0, 0.0, 0.0, 0.0]
tspan = (0.0,30.0)
#with_logger(TerminalLogger()) do
prob =ODEProblem(ODEFunction(system!, mass_matrix = M),u0,tspan, p)
sol =solve(prob,Rodas5(), reltol =1e-8, abstol =1e-8, callback = cb)
Error & Stacktrace ⚠️
Stacktrace when discrete callback activated
No error thrown but results are erroneous.
The equation du[5] = (u[4] -ΔPe)/(2*H) should have a linear increase because in this example u[4] =0.06 after saturation through callback and ΔPe =0.05 . However the results are static; as if the integrator "froze" on the its last value.
Stacktrace when discrete callback deactivated
Warning: At t=1.722792175267728, dt was forced below floating point epsilon 2.220446049250313e-16, and step error estimate =4041.2061747989337. Aborting. There is either an error in your model specification or the true solution is unstable (or the true solution can not be represented in the precision of Float64).
Because discrete callbacks resolve immediately after continuous callbacks, so the DiscreteCallback would be true on the same time as the ContinuousCallback affect!. Is that what you wanted or did you want it to take a step with the new dynamics first? Because if that's the case, then integrator.p[3] == false in the first trigger, which means s_tplus[4] < 0.06 and it just turns off the DiscreteCallback before it does anything? That does not seem like sensible logic.
I first wrote this example while having a wrong understanding of the interpolar's capabilities. You already told me on Julia discourse that modifying the problem won't cause the interpolator to be updated during that integration step. That initial wrong understanding is the reason why the example is ill-conceived.
My intent was to extrapolate the futur ODE's output under a different configuration (du[4] = 0) of the problem that was being solved. (is that even possible ?)
So to answer your question, I wanted the discrete callback to start monitoring the output just after the continuous callback had fired. Because as the timestep is adaptive, I feared that the timestepping could be so big that I would miss the right time for deactivating the saturation.
Describe the bug 🐞
Simply put, I am trying to implement a saturation feature on ODEs' output by detecting a threshhold value via a continuous callback.
Then via a discrete callback I can check if yes or no I can deactivate the saturation. The problem is that once the continuous callback triggers and the problem is modified (i.e. activate the saturation by flipping a boolean state) two strange behaviours happen :
Expected behavior
affect
function of the continuous callback. However an error is thrown when the problem is modified.Minimal Reproducible Example 👇
Error & Stacktrace⚠️
Environment :
using Pkg; Pkg.status()
using Pkg; Pkg.status(; mode = PKGMODE_MANIFEST)
versioninfo()
The text was updated successfully, but these errors were encountered: