My code is much more complex, but I can reproduce the issue with the following example:
import numpy as np
from scipy.integrate import solve_ivp
def funct(t, y):
return -np.sqrt(y)
def event(t, y):
return y[0]-0.1
if __name__ == '__main__':
event.terminal = True
sol = solve_ivp(funct, t_span=[0, 3], y0=[1], events=event)
In this example, the solver overshoots the event and calls funct with y < 0. As one can expect, funct raises a warning and returns NaN. In this specific example, solve_ivp recovers and a solution is still found. However, my real funct uses a third-party package that raises an error when funct is evaluated at an invalid argument, and solve_ivp fails.
Now, I know that the event is computed via an iterative method, so my question would be: is it possible for solve_ivp to detect whether the value of y is going below a given threshold and avoid the error/warning? So, something like "if y < 0 then reject the step" or something similar?
IIUC, the problem here is that
functgets called withy=[0.14553212](before the event) andy=[-0.01227461](after the event) in successive iterations because the step size is too large. In this MRE,functreturns NaN whenyis negative, andsolve_ivpis able to recover, find the event, terminate, and return the solution up until the event. In your real code, however,functraises an error when called with invalid input, sosolve_ivpis not able to recover.How about wrapping
funct, detecting the condition that causes the error, and returning a reasonable value (that won't change the solution) rather than callingfunct? Alternatively,trycallingfunct,exceptreturn some reasonable value if an error gets raised?If this doesn't work for some reason, you can probably "reject the step" and try a smaller step if you're willing to use the
OdeSolverobjects directly assolve_ivpdoes. But no,solve_ivpdoesn't have a mechanism to implement that logic.