I observed this with the latest version of julia, Running on a Windows 11 PC.
tl dr: I have an exception being thrown in a callback for a solve command that’s inside another solve command. I have a try-catch block to handle the exception. However, the outer solve command fails due to the exception. This doesn’t happen when the exception is thrown outside of the callback. This happens regardless of whether I make a custom exception or cause a built-in exception.
Minimal Working Example:
module julia_try_catch_ode
export startup
this line is commented out in the MWE # struct SpatialError <: Exception end
function startup()
@eval using DifferentialEquations
dfdt_0 = [0.0]
tbounds = [0.0, 5.0]
dfdt_params =
dfdt_prob = invokelatest(ODEProblem,dfdt, dfdt_0, tbounds, dfdt_params)
dfdt_sol = invokelatest(solve,dfdt_prob)
end
function dfdt(df, f, p, t)
df = [-1.0]
internal_IC = [0.0]
internal_bounds = [0.0, 5.0]
internal_params =
test_callback = DiscreteCallback(example_callback, sqrt(-1)) # throw(SpatialError)
internal_prob = invokelatest(ODEProblem,dfdt_2, internal_IC, internal_bounds, internal_params)
try
doomed_solve = invokelatest(solve,internal_prob, callback = x_first_callback)
catch e
print(“doomed solver included. No custom exception. \n”)
print(t)
print(“\n”)
print(“e\n”)
print(e)
print(“\n”)
end
return df
end
function dfdt_2(df, f, p, t)
df = [-1.0]
return df
end
function example_callback(var, t, integrator)
return true
end
end
Why does it matter?
I am solving a system that varies in space and time. My solution has the following general structure:
Outer call to solve for change over time
Inner call to solve for spatial change that’s computed at every time step of the outer solver
The inner call has a callback that is meant to prevent a square root domain error under certain conditions. Particularly sometimes an error in the time-dependent solver causes the inner solver to enter a state where square roots of negative numbers will be requested. This indicates instability, and means the spatial operation needs to be terminated. If I don’t terminate it, an error will occur.
Unfortunately, the spatial function is extremely stiff, so radau() is the only method that can handle it. The terminate! callback is not implemented for radau(). Thus, as a workaround, I made a callback that throws a custom exception, and a try-catch block that handles this exception.
Unfortunately, this causes the outer ODE solver to fail.
As you can see from the MWE above, a custom exception isn’t needed to see this behavior. Nor is radau() required. Any exception in a callback, even if handled with try-catch, will cause the outer solver to fail.
I believe this is a bug, so I want to bring it to the developer’s attention. I would also appreciate any suggestions from the community for a workaround.
I don’t want to remove the callback and just make my try-catch exempt the square root domain error (this would “fix” the MWE) because then I wouldn’t see if there’s a domain error due to an unexpected bug. I know about the specific parameter values that cause this specific domain error, so I only want to stop the spatial integration and retry with a different set of parameters under that specific condition, not whenever a domain error occurs.