Search code examples
pythonnumpyderivative

Numerical calculation of curvature


I would like to calculate local curvature i.e at each point. I have a set of data points that a equally spaced in x. Below is the code for generating curvature.

data=np.loadtxt('newsorted.txt') #data with uniform spacing
x=data[:,0]
y=data[:,1]

dx = np.gradient(data[:,0]) # first derivatives
dy = np.gradient(data[:,1])

d2x = np.gradient(dx) #second derivatives
d2y = np.gradient(dy)

cur = np.abs(d2y)/(1 + dy**2))**1.5 #curvature

Below is the image of curvature (magenta) and its comparison with analytical (equation: -0.02*(x-500)**2 + 250)(solid green) curvature

Why does there is so much deviation between the two? How to get exact values that of analytical.

Help appreciated.


Solution

  • I've been playing a bit with your values and i've found that they aren't smooth enough to compute curvature. In fact, even the first derivative is flawed. Here is why : Few plots of given data and my interpolation

    You can see in blue your data looks like a parabola, and it's derivative should look like a straight line but it does not. And it get worse when you take the second derivative. In red this is a smooth parabola computed with 10000 points (tried with 100 points, it works the same : perfect lines and curvature). I made a little script to 'enrich' your data, increasing artificially the number of points but it only get worse, here is my script if you want to try.

    import numpy as np
    import matplotlib.pyplot as plt
    
    def enrich(x, y):
        x2 = []
        y2 = []
        for i in range(len(x)-1):
            x2 += [x[i], (x[i] + x[i+1]) / 2]
            y2 += [y[i], (y[i] + y[i + 1]) / 2]
        x2 += [x[-1]]
        y2 += [y[-1]]
        assert len(x2) == len(y2)
        return x2, y2
    
    data = np.loadtxt('newsorted.txt')
    x = data[:, 0]
    y = data[:, 1]
    
    for _ in range(0):
        x, y = enrich(x, y)
    
    dx = np.gradient(x, x)  # first derivatives
    dy = np.gradient(y, x)
    
    d2x = np.gradient(dx, x)  # second derivatives
    d2y = np.gradient(dy, x)
    
    cur = np.abs(d2y) / (np.sqrt(1 + dy ** 2)) ** 1.5  # curvature
    
    
    # My interpolation with a lot of points made quickly
    x2 = np.linspace(400, 600, num=100)
    y2 = -0.0225*(x2 - 500)**2 + 250
    
    dy2 = np.gradient(y2, x2)
    
    d2y2 = np.gradient(dy2, x2)
    
    cur2 = np.abs(d2y2) / (np.sqrt(1 + dy2 ** 2)) ** 1.5  # curvature
    
    plt.figure(1)
    
    plt.subplot(221)
    plt.plot(x, y, 'b', x2, y2, 'r')
    plt.legend(['new sorted values', 'My interpolation values'])
    plt.title('y=f(x)')
    plt.subplot(222)
    plt.plot(x, cur, 'b', x2, cur2, 'r')
    plt.legend(['new sorted values', 'My interpolation values'])
    plt.title('curvature')
    plt.subplot(223)
    plt.plot(x, dy, 'b', x2, dy2, 'r')
    plt.legend(['new sorted values', 'My interpolation values'])
    plt.title('dy/dx')
    plt.subplot(224)
    plt.plot(x, d2y, 'b', x2, d2y2, 'r')
    plt.legend(['new sorted values', 'My interpolation values'])
    plt.title('d2y/dx2')
    
    plt.show()
    

    My recommendation would be to interpolate your data with a parabola and compute as many points on this interpolation to work with.