Need help solving a second order non-linear ODE in python

前端 未结 2 758
迷失自我
迷失自我 2020-12-15 10:55

I don\'t really know where to start with this problem, as I haven\'t had much experience with this but it is required to solve this part of the project using a computer.

相关标签:
2条回答
  • 2020-12-15 11:21

    To solve a second-order ODE using scipy.integrate.odeint, you should write it as a system of first-order ODEs:

    I'll define z = [x', x], then z' = [x'', x'], and that's your system! Of course, you have to plug in your real relations:

    x'' = -(b*x'(t) + k*x(t) + a*(x(t))^3 + m*g) / m
    

    becomes:

    z[0]' = -1/m * (b*z[0] + k*z[1] + a*z[1]**3 + m*g)
    z[1]' = z[0]

    Or, just call it d(z):

    def d(z, t):
        return np.array((
                         -1/m * (b*z[0] + k*z[1] + a*z[1]**3 + m*g),  # this is z[0]'
                         z[0]                                         # this is z[1]'
                       ))
    

    Now you can feed it to the odeint as such:

    _, x = odeint(d, x0, t).T
    

    (The _ is a blank placeholder for the x' variable we made)

    In order to minimize b subject to the constraint that the maximum of x is always negative, you can use scipy.optimize.minimize. I'll implement it by actually maximizing the maximum of x, subject to the constraint that it remains negative, because I can't think of how to minimize a parameter without being able to invert the function.

    from scipy.optimize import minimize
    from scipy.integrate import odeint
    
    m = 1220
    k = 35600
    g = 17.5
    a = 450000
    z0 = np.array([-.5, 0])
    
    def d(z, t, m, k, g, a, b):
        return np.array([-1/m * (b*z[0] + k*z[1] + a*z[1]**3 + m*g), z[0]])
    
    def func(b, z0, *args):
        _, x = odeint(d, z0, t, args=args+(b,)).T
        return -x.max()  # minimize negative max
    
    cons = [{'type': 'ineq', 'fun': lambda b: b - 1000, 'jac': lambda b: 1},   # b > 1000
            {'type': 'ineq', 'fun': lambda b: 10000 - b, 'jac': lambda b: -1}, # b < 10000
            {'type': 'ineq', 'fun': lambda b: func(b, z0, m, k, g, a)}] # func(b) > 0 means x < 0
    
    b0 = 10000
    b_min = minimize(func, b0, args=(z0, m, k, g, a), constraints=cons)
    
    0 讨论(0)
  • 2020-12-15 11:29

    I don't think you can solve your problem as stated: your initial conditions, with x = 0 and x' > 0 imply that the solution will be positive for some values very close to the starting point. So there is no b for which the solution is never positive...

    Leaving that aside, to solve a second order differential equation, you first need to rewrite it as a system of two first order differential equations. Defining y = x' we can rewrite your single equation as:

    x' = y
    y' = -b/m*y - k/m*x - a/m*x**3 - g
    
    x[0] = 0, y[0] = 5
    

    So your function should look something like this:

    def fun(z, t, m, k, g, a, b):
        x, y = z
        return np.array([y, -(b*y + (k + a*x*x)*x) / m - g])
    

    And you can solve and plot your equations doing:

    m, k, g, a = 1220, 35600, 17.5, 450000
    tmax, dt = 10, 0.01
    t = np.linspace(0, tmax, num=np.round(tmax/dt)+1)
    for b in xrange(1000, 10500, 500):
        print 'Solving for b = {}'.format(b)
        sol = odeint(fun, [0, 5], t, args=(m, k, g, a, b))[..., 0]
        plt.plot(t, sol, label='b = {}'.format(b))
    plt.legend()
    

    enter image description here

    0 讨论(0)
提交回复
热议问题