Search code examples
pythonodeodeintgenfromtxt

Finding zero crossing in python


I have written the following code to see in which t my ODE "exponential_decay" crosses the zero line. this is Brent Method.

odr, hr, dr, cr, m = np.genfromtxt('data.txt',unpack=True)
n=0
with open('RDE_nob_trans.txt', 'w') as d:
  for i in range(len(dr)):
      c = cr[i]
      initp = dr[i]
      exponential_decay = lambda t, y: -(1/(1+t)) * (2 *(1-y)* (-2 + (y/c)) + 3 - 3 * y)
      t_span = [0, 1] # Interval of integration
      y0 = [initp] # Initial state: y(t=t_span[0])=2
      desired_answer = odr[i]
      sol_ode = solve_ivp(exponential_decay, t_span, y0) # IVP solution

      f_sol_ode = interp1d(sol_ode.t, sol_ode.y) # Build interpolated function
      ans = brentq(lambda x: f_sol_ode(x) - desired_answer, t_span[0], t_span[1])
      d.write("{0} {1} {2} {3} {4}\n".format(hr[i], dr[i], cr[i], m[i], ans))

In this code we know the initial point initp = dr[i], we know the value of the differential equation at the zero crossing desired_answer = odr[i], and we are willing to find in which t we have this answer. It is OK and we get the answer by this code. ans is the t at the zero crossing.

My problem is what should we do if our answer of ODE which now is desired_answer = odr[i] is not just a number and is a value in terms of t.

I mean using odr[i] we are reading the data file and subsequently reading numbers. Now consider we have someting like odr = 0.1 * t, 0.12 *t, 0.23 *t etc. which is not a number and is a function in terms of t.


Solution

  • This is not the most efficient use of the solve_ivp interface. You can get your result automatically using the event mechanism.

    def event(t,y): return y-desired_answer;
    event.terminal = True;
    
    sol_ode = solve_ivp(exponential_decay, t_span, y0, events=event) # IVP solution
    ans = sol_ode.t[-1]
    

    Even if you want to use your own solver (or solver call), you can get a method-specific and accurate piecewise polynomial interpolation using the dense output option,

    sol_ode = solve_ivp(exponential_decay, t_span, y0, dense_output=True) # IVP solution
    f_sol_ode = sol_ode.sol
    

    To get the roots of a function that depends on the time, you just have to code that time dependence, for instance as in

    def event(t,y): return y-0.23*t;
    

    or

    ans = brentq(lambda t: f_sol_ode(t) - 0.23*t, t_span[0], t_span[1])
    

    If you want reliable results, you should explicitly control the tolerances atol, rtol.