MATLAB FFT 微分

计算科学 matlab 有限差分 傅立叶分析
2021-12-23 17:59:53

我正在尝试使用傅里叶变换微分(https://en.wikibooks.org/wiki/Parallel_Spectral_Numerical_Methods/Finding_Derivatives_using_Fourier_Spectral_Methods#Taking_a_Derivative_in_Fourier_Space)来实现拉普拉斯算子。我正在将傅里叶微分与真实空间中的周期性拉普拉斯算子进行比较。

当我的函数是 cos(x)*cos(y) 时,两个结果(实数和傅里叶空间)相互一致: 在此处输入图像描述

但是,当我的函数是 sin(x)*sin(y) 时,傅里叶空间拉普拉斯算子中有奇怪的振荡(一般形状保持不变)。在下图中,可以看到左图(傅里叶空间微分)有点模糊:

在此处输入图像描述

通过左上图的示例横截面如下所示: 在此处输入图像描述

我很困惑为什么会出现这些振荡,我希望有一个解决方案。我怀疑这可能与混叠有关,但我一直无法查明问题或解决方案。

请在此处找到实现傅立叶空间拉普拉斯算子的代码:

Nx = 512;
Ny = 256;

Lx = 2 * pi;
Ly = 4 * pi;

x = linspace(0, Lx, Nx)';
y = linspace(0, Ly, Ny)';

dx = x(2) - x(1);
dy = y(2) - y(1);

% Fourier space vectors
kx = 2*pi/Lx*[0:Nx/2-1 0 -Nx/2+1:-1]'; 
ky = 2*pi/Ly*[0:Ny/2-1 0 -Ny/2+1:-1]';

[x_grid, y_grid] = meshgrid(x, y);
[kx_grid, ky_grid] = meshgrid(kx, ky);

v = sin( 2*pi*x_grid ./ Lx ) .* sin( 2*pi*y_grid ./ Ly ); % Test Function
v_hat = fft2(v); % FFT of test function

% Fourier Space Laplacian
w_L = -( kx_grid.^2 + ky_grid.^2 ) .* v_hat;
w_L = real(ifft2(w_L));

% Periodic Real Space Laplacian
L = Laplacian_2D(v, dx, dy);

c = 10; % Because the edges get very weird
figure; mesh(x(c + 1:end-c),y(c + 1:end-c),w_L(c + 1:end-c,c+1:end-c)); title('Fourier');
figure; mesh(x(c + 1:end-c),y(c + 1:end-c),L(c + 1:end-c,c+1:end-c)); title('Real Space');

也请在这里找到我的 2D 周期性拉普拉斯算子:

function out = Laplacian_2D( f, hx, hy )

    f = f';
    [r c] = size(f);
    out = zeros(r, c);

    out( 2 : end - 1, 2 : end - 1 ) = ...
        ( f( 3 : end, 2 : end - 1 ) - ...
        2 * f( 2 : end - 1, 2 : end - 1 ) + ...
        f( 1 : end - 2, 2 : end - 1 ) ) / hx^2 + ...
        ( f( 2 : end - 1, 3 : end ) - ...
        2 * f( 2 : end - 1, 2 : end - 1 ) + ...
        f( 2 : end - 1, 1 : end - 2 ) ) / hy^2 ;

    % Remember (i, j)
    %  i = row, j = column
    %  i = y, j = x
    % but we transposed so we can say i = x, j = y

    % Periodic boundary conditions

    % 4 corners
    out( 1, 1 ) = ...
        ( f(2, 1) - 2 * f(1, 1) + f(end, 1) ) / hx^2 + ...
        ( f(1, 2) - 2 * f(1, 1) + f(1, end) ) / hy^2;
    out(1, end) = ...
        ( f(2, end) - 2 * f(1, end) + f(end, end) ) / hx^2 + ...
        ( f(1, 1) - 2 * f(1, end) + f(1, end - 1) )/ hy^2;
    out(end, 1) = ...
        ( f(1, 1) - 2 * f(end, 1) + f(end - 1, 1) ) / hx^2 + ...
        ( f(end, 2) - 2 * f(end, 1) + f(end, end) ) / hy^2;
    out(end, end) = ...
        ( f(1, end) - 2 * f(end, end) + f(end - 1, end) ) / hx^2 + ...
        ( f(end, 1) - 2 * f(end, end) + f(end, end - 1) ) / hy^2;

    % edges
    out( 1, 2 : end - 1 ) = ...
        ( f(2, 2 : end - 1) - 2 * f(1, 2 : end - 1) + f(end, 2 : end - 1) ) / hx^2 + ...
        ( f(1, 3 : end) - 2 * f(1, 2 : end - 1) + f(1, 1 : end - 2) ) / hy^2;
    out( end, 2 : end - 1 ) = ...
        ( f(1, 2 : end - 1) - 2 * f(end, 2 : end - 1) + f(end - 1, 2 : end - 1) ) / hx^2 + ...
        ( f(end, 3 : end) - 2 * f(end, 2 : end - 1) + f(end, 1 : end - 2) ) / hy^2;

    out( 2 : end - 1, 1) = ...
        ( f(3 : end, 1) - 2 * f(2 : end - 1, 1) + f(1 : end - 2, 1) ) / hx^2 + ...
        ( f(2 : end - 1, 2) - 2 * f(2 : end - 1, 1) + f(2 : end - 1, end) ) / hy^2;
    out( 2 : end - 1, end) = ...
        ( f(3 : end, end) - 2 * f(2 : end - 1, end) + f(1 : end - 2, end) ) / hx^2 + ...
        ( f(2 : end - 1, 1) - 2 * f(2 : end - 1, end) + f(2 : end - 1, end - 1) ) / hy^2;

    out = out';

end
2个回答

1)您误解了 fft2 输出元素的存储顺序。这是一个非常常见的错误,因为它很容易混淆。值得庆幸的是,有一种简单的方法可以避免此类错误。以“自然方式”排列傅里叶空间向量

% Fourier space vectors kx = 2*pi/Lx*[-Nx/2:Nx/2-1]'; ky = 2*pi/Ly*[-Ny/2:Ny/2-1]';

然后,使用 ifft 函数确保 fft2 输出具有相同的排列。这几乎不会给您的代码增加任何计算负载。

v_hat = fftshift(fft2(v)); % FFT of test function

像以前一样在傅立叶空间中执行代数,然后通过使用 ifftshift 函数对 ifft2 的输入进行逆移。

w_L = real(ifft2(ifftshift(w_L)));

2)正如你所注意到的,事情在边缘变得“非常奇怪”。这是另一个问题,与错误或数值准确性无关,而是与傅里叶级数变换的收敛特性有关。当待变换函数为连续时,傅里叶级数具有绝对收敛性。但是,当存在不连续性时,傅里叶级数按范数收敛,这意味着只有截断级数与其极限之间的差异的度量会趋于无穷大,因此这些边缘处的“奇怪”振荡,称为“吉布斯振荡”。

但是,可能不清楚不连续性的来源是什么。毕竟,v 及其导数是表现良好的函数。这与傅里叶级数变换(FST)和离散傅里叶变换(DFT)的差异有关。FST 作用于周期函数,而 DFT 作用于有限长度的离散信号。为了将 DFT 解释为 FFT 的数值近似,我们隐含地假设 DFT 的输入表示某个周期信号的样本,其半周期由该窗口表示,另一半由其镜像表示。当然,这在一维情况下更容易可视化。因此,在窗口边缘处样本不为零的每个函数都被隐式解释为不连续的。v 在边缘可能为零,

我刚刚尝试运行它,它似乎消除了振荡,所以我将其添加为答案。我建议您按如下方式更改代码,在您的 linspace 坐标定义之前,添加并修改要读取的行:

 x = linspace(0, Lx, Nx+1)'; x = x(1:Nx);
 y = linspace(0, Ly, Ny+1)'; y = y(1:Ny);

我建议你写出方程的离散形式,看看为什么不应该包括最后一点(如果你确实包括它,它会重复)。

感谢@Richard Zhang,我更新了我的答案,因为我最初的建议容易受到精度错误的影响。