Search code examples
pythonscipyfftscientific-computing

Computing numeric derivative via FFT - SciPy


I wrote the following code to compute the approximate derivative of a function using FFT:

from scipy.fftpack import fft, ifft, dct, idct, dst, idst, fftshift, fftfreq
from numpy import linspace, zeros, array, pi, sin, cos, exp
import matplotlib.pyplot as plt

N = 100
x = linspace(0,2*pi,N)

dx = x[1]-x[0]
y = sin(2*x)+cos(5*x)
dydx = 2*cos(2*x)-5*sin(5*x)

k = fftfreq(N,dx)
k = fftshift(k)

dydx1 = ifft(-k*1j*fft(y)).real

plt.plot(x,dydx,'b',label='Exact value')
plt.plot(x,dydx1,'r',label='Derivative by FFT')
plt.legend()
plt.show()

However, it is giving unexpected results, which I believe is related to the incorrect input of the wavenumbers given by the array k:

Comparison of the exact and approximate derivative

I know that different implementations of the FFT handle the wavenumbers order differently, so what am I missing here? Any ideas will be very appreciated.


Solution

  • I think that the issue comes from fftfreq which does not do what you think it does as you can read in the doc.

    Also, there is an anecdotal negative sign I don't understand in your code.

    Oh and, for information, the fftpack.diff does exactly what you want to achieve.

    Here is a code sample that does the job:

    import numpy as np
    import matplotlib.pyplot as plt
    
    from scipy import fftpack
    
    N = 100
    L = 2*np.pi
    dx = L/N
    x = np.linspace(0,L,N)
    
    y = np.sin(2*x)+np.cos(5*x)
    dydx = 2*np.cos(2*x)-5*np.sin(5*x)
    
    fhat = np.fft.fft(y)
    
    k = (2*np.pi/L)*np.arange(0,N)
    k = fftpack.fftshift(k)
    
    dydx1 = fftpack.ifft(k*1j*fftpack.fft(y)).real
    
    plt.plot(x,dydx,'b',label='Exact value')
    plt.plot(x,dydx1,'r',label='Derivative by FFT')
    plt.plot(x,fftpack.diff(y),'g',label='Derivative by FFT 2')
    plt.legend()
    plt.show()