Python/NumPy - 二维FFT无法得到解析解
我正在编写一个代码,作为第一步计算一个函数的二维FFT。我用函数exp(-r)/r进行测试,其中r = sqrt(x²+y²),它的解析Hankel变换是1/sqrt(r²+1)。零阶Hankel变换与二维傅里叶变换之间通过一个重新缩放相关联。然而,我无法恢复这个解析解。我的变换比它更窄,且有非零的虚部。
最小可复现示例:
import numpy as np
import matplotlib.pyplot as pl
def min_pot(x: np.ndarray, y: np.ndarray):
xbig, ybig = np.meshgrid(x, y)
r = np.sqrt(np.square(xbig) + np.square(ybig))
r0 = np.unravel_index(np.argmin(r, axis=None), r.shape)
test = np.exp(-r)*np.reciprocal(r)
test[r0] = 2*test[r0[0]+1, r0[0]] - test[r0[0]+2, r0[0]] #this suppresses the r=0 singularity
return test
x_min = np.linspace(-20, 20, num=1001)
y_min = np.linspace(-20, 20, num=1001)
real_a = min_pot(x_min, y_min)
shift_a = np.fft.ifftshift(real_a)
fourier_a = np.fft.fftshift(np.fft.fft2(np.fft.fftshift(real_a), norm="forward"))
kx = np.fft.fftshift(np.fft.fftfreq(len(x_min), d=(x_min[1]-x_min[0])))
#we represent a cut in the x axis
analytic = np.power(np.square(kx) + 1, -.5)
max_real = np.amax(fourier_a[fourier_a.shape[0]//2, :].real # normalization to compare curve shapes better
fig, ax = pl.subplots(1, 1)
ax.plot(kx, fourier_a[fourier_a.shape[0]//2, :].real/max_real), label='Real fft')
ax.plot(kx, fourier_a[fourier_a.shape[0]//2, :].imag/max_imag), label='Imag. fft')
ax.plot(kx, analytic, label='Analytic')
pl.legend()
pl.show()
结果图是:
有人知道我到底哪里做错了吗?
解决方案
这是一个不完整的回答,因为我无法准确看到你到底在做错什么,也不使用Python。凭直觉从你的图看,FFT的结果在宽度上似乎被放大了 1/2pi 倍,所以我在Excel做了一个快速探索性模型,以你的图为背景。解析函数普适化到 f(x,a) = a/sqrt(a^2+x^2),因此它可以很容易生成一个归一化到 1 的图,且具有变化的全宽半最大值(FWHM)。
你的解析形式对于 a=1 来说是完全正确的。我的粗略模型在X 值较小时能很好拟合FFT结果,但当X 增大时发散,因为该函数理应渐近趋向于 a/x,但并非如此。
我把Excel的解析解 1/sqrt(1+x^2) 用细黑线绘在绿色之上以强调拟合,同时用红色标出对FFT结果的最佳(肉眼)拟合点。请注意,当 x = 10 时,尺度为宽度的解析解的翼缘仍然相当高,达到约 0.016。我还把拟合在 x=10 限制,以便展示背后原始曲线的一部分。
神秘的是,你基于FFT的计算收敛得太好。它低于解析解,迅速触及零基线,而我本以为混叠会让它略高于(解析结果)。
在较大 x 处应该有更高的基线,因为函数在缓慢下降。非零的虚部几乎可以被视为在问题设定中的对称性错误的象限标记。一个偶函数在其FFT中永远不可能有虚部(除非是累积舍入误差)。
要纠正数组的对称性,可以尝试只用10、11或 100、101个元素来查看并打印它们,以发现象限标记的错位。我怀疑这样的修正可能不足以解决问题,但应在大致的 x 数值区间内提高一致性。大约一百个点在这个范围内的变换看起来应该足够,十个可能太少。
不妨用 N=1000 或任意其他偶数进行代码尝试,看看相位误差是否仍然存在。否则,由于你知道输入函数是纯实数的,理想情况下应使用FFT的实部到共轭复数形式变换 np.rfft2,它将使用一半的内存、速度更快,且可能略微更准确。如果你今后经常执行这种变换,我强烈推荐后者。
