Search code examples
pythonnumpyfftphysicsfftw

Addressing Zero Frequency Issue in Fourier Transform for Electrostatics Problems


"I'm looking to utilize a discrete Fourier transform (DFT) to tackle an electrostatics problem, but I've encountered an issue with the zero frequency. To illustrate this, I attempted a basic problem. If you could guide me on how to handle the division and obtain a sine function, which is the correct solution to this equation, it would lift a weight off my shoulders."

Limit in zero

When I do something like this:

$$ \\frac{d\\phi(x)}{dx} = cos(x) $$
which solution is $\\phi(x) = sin(x)$ 

If you take the limit as $k$ approaches zero, the first frequency becomes zero. Therefore, you could use zero for the first value, and that resolves everything.

import numpy as np
import matplotlib.pyplot as plt

# Define the parameters

num_points = 10000  # Number of points in the signal
delta_x = 0.01      # Spacing between points in the space domain
x = np.linspace(0, (num_points - 1) * delta_x, num_points)

# Define the cosine function of x

y = np.cos(x)

# Compute the Fourier transform of the cosine of x

Y = np.fft.rfft(y)

# Compute the corresponding frequencies

k_values = 2*np.pi * np.fft.rfftfreq(num_points, delta_x)

# Apply a mask to avoid the first value in the frequencies

k_values_masked = k_values[1:]  # Avoid the first value

# Compute the Fourier transform of the cosine of x divided by ik, handling the limit at k = 0

masked_Y_divided = np.ones_like(Y, dtype=np.complex128)
masked_Y_divided[1:] = Y\[1:] / (1j * k_values_masked)

# Perform the inverse Fourier transform to obtain the solution

reconstructed_y = np.fft.irfft(masked_Y_divided)

# Plot the result

plt.figure(figsize=(10, 6))

plt.subplot(2, 1, 1)
plt.plot(x, y, label='Cosine of x')
plt.title('Cosine of x')
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True)
plt.legend()

plt.subplot(2, 1, 2)
plt.plot(x, reconstructed_y.real, label='Reconstructed sine of x')
plt.plot(x, np.sin(x), label='Expected result')
plt.title('Reconstructed sine of x')
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True)
plt.legend()

plt.tight_layout()
plt.show()

I expect to obtain a sine function, but I encounter issues with the general case, especially when I'm unsure of the answer and have no idea why the solutions for the amplitudes are also affected. Additionally, the solution I'm obtaining is inclined compared to what it should be.


Solution

  • Some of your issues stem from a poor choice of FFT length. Making a wild guess, this is closer to what you expect:

    import numpy as np
    import matplotlib.pyplot as plt
    
    # Number of points in the signal, multiple of the cosine's angular velocity
    num_points = int(round(1e3 * np.pi))
    delta_x = 0.01       # Spacing between points in the space domain
    x = np.linspace(start=0, stop=(num_points - 1)*delta_x, num=num_points)
    y = np.cos(x)
    Y = np.fft.rfft(y)
    
    # Actually the angular velocities
    omega = 2*np.pi * np.fft.rfftfreq(n=num_points, d=delta_x)
    
    # Avoid the first value in the frequencies
    Y[0] = 0
    omega[0] = 0.1
    
    # Compute the Fourier transform of the cosine of x divided by ik, handling the limit at k = 0
    masked_Y_divided = Y / (1j * omega)
    
    # Perform the inverse Fourier transform to obtain the solution
    reconstructed_y = np.fft.irfft(masked_Y_divided)
    
    fig, (ax_t, ax_f) = plt.subplots(ncols=2)
    ax_t.plot(x, y, label='Cosine of x')
    ax_t.plot(x, reconstructed_y.real, label='Reconstructed sine of x')
    ax_t.set_xlabel('x')
    ax_t.set_ylabel('y')
    ax_t.grid(True)
    ax_t.legend()
    ax_f.plot(omega[:40], np.abs(Y[:40]))
    ax_f.set_xlabel('omega')
    ax_f.set_ylabel('Y')
    
    plt.show()
    

    sine and spectrum