Search code examples
pythonmathphysicsdifferential-equationsrunge-kutta

Runge–Kutta methods for ODE integration in Python with additional constraints


I have a question on solving second order differential equations using RK4, considering additional constraints on the first derivative. I am doing the example shown here with some modifications

θ′′(t) + b θ′(t) + c sin(θ(t)) = 0

The additional constraint is:

when θi θ(i+1)<0, then θ′(i+1)=0.9θ′i,

where i is the steps of t, i+1 is one step after i. In the real world, it says when the direction of displacement reverses, its velocity is reduced to 90%.

Vectorially if y(t) = (θ(t), ω(t)), then the equation is = f(t,y), where f(t,y) = (y₂(t), −by₂(t) − cos(y₁(t))).

In this problem, how should I modify the code if I want to add constraints on ω or θ′(t) (which are the same thing)? Here is my code which didn't work. The additional condition makes θ′ non-continuous. The following "homemade" solution cannot update θ′ properly. I am new to Python and this is my first StackOverflow post. Any guidance is much appreciated.

def rungekutta4(f, y0, t, args=()):
    n = len(t)
    y = np.zeros((n, len(y0)))
    y[0] = y0
    for i in range(n - 1):
        h = t[i+1] - t[i]
        if y[i][0]*y[i+1][0]<0:
            k1 = f(y[i], t[i], *args)
            k2 = f(y[i] + k1 * h / 2., t[i] + h / 2., *args)
            k3 = f(y[i] + k2 * h / 2., t[i] + h / 2., *args)
            k4 = f(y[i] + k3 * h, t[i] + h, *args)
            y[i+1] = y[i] + (h / 6.) * (k1 + 2*k2 + 2*k3 + k4)*0.9
        else:
            k1 = f(y[i], t[i], *args)
            k2 = f(y[i] + k1 * h / 2., t[i] + h / 2., *args)
            k3 = f(y[i] + k2 * h / 2., t[i] + h / 2., *args)
            k4 = f(y[i] + k3 * h, t[i] + h, *args)
            y[i+1] = y[i] + (h / 6.) * (k1 + 2*k2 + 2*k3 + k4)        
    return y

Solution

  • In the current formulation and taking the idea that each time the pendulum passes the vertical position its velocity is reduced by 10%, this can be approximately arranged as

        for i in range(n - 1):
            h = t[i+1] - t[i]
            y[i+1] = RK4step(f,t[i],y[i],h, args)
            if y[i+1,0]*y[i,0] < 0: y[i+1,1] *= 0.9
        return y
    

    that is, first compute the new value and then apply the condition. The time step should be small enough that the angle only changes a few degrees. For larger time steps you would have to split the step containing the zero crossing, using some root finding method like the secant method to find a more accurate time of the root.