如何在2维或更多维的FFTW中进行归一化?

时间:2018-06-20 14:38:04

标签: fortran fft fftw

我正在尝试弄清楚如何使用FFTW正确归一化DFT的结果。 FFTW tutorial指出,大小为n的1d复数数组X的正向(FFTW_FORWARD)离散傅里叶变换将计算数组Y,其中

Y_k = \ sum \ limits_ {j = 0} ^ {n-1} X_j e ^ {-2 \ pi j k \ sqrt {-1} / n}

后向DFT计算:

Y_k = \ sum \ limits_ {j = 0} ^ {n-1} X_j e ^ {+ 2 \ pi j k \ sqrt {-1} / n}

这些定义与实数到复杂的转换相同。

此外,本教程指定“ FFTW计算未归一化的变换,因为DFT中求和的前面没有系数。换句话说,应用正向变换然后向后变换会将输入乘以n。 ” 但是,它没有指定在何处进行此重新缩放。我想这可能取决于应用程序,但不确定如何正确使用它。 This answer指出应将其规范化为正向,但我对此表示怀疑,我将对此进行详细说明。

我的目标是弄清楚如何正确地标准化FFT结果,以便获得我所期望的结果。因此,我首先做了一个简单的1D变换,在那儿我确切地知道了什么:变换

1/2(δ(1 + x)-δ(1-x))

以δ为狄拉克三角洲函数,我希望得到:

integral _(-∞)^∞(1/2(δ(1 + x)-δ(1-x)))e ^(-2πiωx)dx = i sin(2πω)

当我对i sin(2πω)进行IFFT时也是如此,只是现在我需要除以n来归一化。

这是我用来演示此行为的代码:

program use_fftw

  use,intrinsic :: iso_c_binding
  implicit none
  include 'fftw3.f03'


  integer, parameter   :: N = 1000
  integer, parameter   :: dp = kind(1.d0)
  real(dp), parameter  :: pi = 3.1415926d0
  real(dp), parameter  :: physical_length = 500
  real(dp), parameter  :: dx = physical_length/real(N)
  real(dp), parameter  :: dk = 1.d0 / physical_length

  integer :: i, ind1, ind2


  ! for double precision: use double complex & call dfftw_plan_dft_1d
  complex(C_DOUBLE_COMPLEX), allocatable, dimension(:) :: arr_out
  real(C_DOUBLE),    allocatable, dimension(:)         :: arr_in
  type(C_PTR)                                          :: plan_forward, plan_backward


  allocate(arr_in(1:N))
  allocate(arr_out(1:N/2+1))

  plan_forward = fftw_plan_dft_r2c_1d(N, arr_in, arr_out, FFTW_ESTIMATE)
  plan_backward = fftw_plan_dft_c2r_1d(N, arr_out, arr_in, FFTW_ESTIMATE)


  !----------------------
  ! Setup
  !----------------------

  ! add +1: index = 1 corresponds to x=0
  ind1 = int(1.d0/dx)+1                   ! index where x=1
  ind2 = int((physical_length-1.d0)/dx)+1 ! index where x=-1

  arr_in = 0
  arr_in(ind1) = -0.5d0
  arr_in(ind2) = 0.5d0


  !----------------------
  ! Forward
  !----------------------
  call fftw_execute_dft_r2c(plan_forward, arr_in, arr_out)

  write(*,*) "Verification: Max real part of arr_out:", maxval(real(arr_out))

  open(unit=666,file='./fftw_output_norm1d_fft.txt', form='formatted')
  do i = 1, N/2+1
    write(666, '(2E14.5,x)') (i-1)*dk, aimag(arr_out(i))
  enddo
  close(666)

  write(*,*) "Finished! Written results to fftw_output_norm1d_fft.txt"


  !----------------------
  ! Backward
  !----------------------
  call fftw_execute_dft_c2r(plan_backward, arr_out, arr_in)

  arr_in = arr_in/N

  open(unit=666,file='./fftw_output_norm1d_real.txt', form='formatted')
  do i = 1, N
    write(666, '(2E14.5,x)') (i-1)*dx, arr_in(i)
  enddo
  close(666)

  write(*,*) "Finished! Written results to fftw_output_norm1d_real.txt"

  deallocate(arr_in, arr_out)
  call fftw_destroy_plan(plan_forward)
  call fftw_destroy_plan(plan_backward)

end program use_fftw

结果完全符合我的期望:

Result of Fourier transform Result of Inverse Transform of Fourier transform

因此,在这种情况下,我仅在从傅立叶空间回到真实空间并进行归一化时才归一化(除以n)。

但是I ran into problems是我尝试对多个维度进行相同的操作。 这次,我正在尝试转型

sqrt(π/ 2)((δ(-1 + x)-δ(1 + x))δ(y)+δ(x)(δ(-1 + y)-δ(1 + y) ))

应该给出

integral _(-∞)^∞(sqrt(π/ 2)((δ(-1 + x)-δ(1 + x))δ(y)+δ(x)(δ(-1 + y )-δ(1 + y))))e ^(-2πi {x,y} {a,b})d {x,y} = + i sin(a)+ i sin(b)

我绘制了x = 0的结果(分别为k_x = 0): Fourier transform 2d

在窦波的频率和振幅上,这似乎是完全错误的。

但是,通过除以n ^ 2进行反变换和归一化,可以在x和y方向上给出预期的初始条件。这是x = 0的图: inverted fourier transform

我不知道我在做什么错...

这是二维代码:

program use_fftw

  use,intrinsic :: iso_c_binding
  implicit none
  include 'fftw3.f03'


  integer, parameter   :: N = 1000
  integer, parameter   :: dp = kind(1.d0)
  real(dp), parameter  :: pi = 3.1415926d0
  real(dp), parameter  :: physical_length = 500
  real(dp), parameter  :: dx = physical_length/real(N)
  real(dp), parameter  :: dk = 1.d0 / physical_length

  integer :: i, ind1, ind2


  ! for double precision: use double complex & call dfftw_plan_dft_1d
  complex(C_DOUBLE_COMPLEX),  allocatable, dimension(:,:) :: arr_out
  real(C_DOUBLE),             allocatable, dimension(:,:) :: arr_in
  type(C_PTR)                              :: plan_forward, plan_backward


  allocate(arr_in(1:N, 1:N))
  allocate(arr_out(1:N/2+1, 1:N))


  plan_forward = fftw_plan_dft_r2c_2d(N, N, arr_in, arr_out, FFTW_ESTIMATE)
  plan_backward = fftw_plan_dft_c2r_2d(N, N, arr_out, arr_in, FFTW_ESTIMATE)


  !----------------------
  ! Setup
  !----------------------

  ! add +1: index = 1 corresponds to x=0
  ind1 = int(1.d0/dx)+1                     ! get index where x = 1
  ind2 = int((physical_length-1.d0)/dx)+1   ! get index where x = -1

  arr_in = 0
  ! y=0:
  arr_in(ind1, 1) =  sqrt(pi/2)
  arr_in(ind2, 1) =  -sqrt(pi/2)
  ! x=0:
  arr_in(1, ind1) =  sqrt(pi/2)
  arr_in(1, ind2) =  -sqrt(pi/2)


  !----------------------
  ! Forward
  !----------------------
  call fftw_execute_dft_r2c(plan_forward, arr_in, arr_out)

  write(*,*) "Verification: Max real part of arr_out:", maxval(real(arr_out))

  open(unit=666,file='./fftw_output_norm2d_fft_x=0.txt', form='formatted')
  open(unit=667,file='./fftw_output_norm2d_fft_y=0.txt', form='formatted')
  do i = 1, N
    write(666, '(2E14.5,x)') (i-1)*dk, aimag(arr_out(1,i))
  enddo
  do i = 1, N/2+1
    write(667, '(2E14.5,x)') (i-1)*dk, aimag(arr_out(i,1))
  enddo
  close(666)
  close(667)

  write(*,*) "Finished! Written results to fftw_output_normalisation_fft_x.txt and fftw_output_normalisation_fft_y.txt"


  !----------------------
  ! Backward
  !----------------------
  call fftw_execute_dft_c2r(plan_backward, arr_out, arr_in)

  ! Normalisation happens here!
  arr_in = arr_in/N**2

  open(unit=666,file='./fftw_output_norm2d_real_x=0.txt', form='formatted')
  open(unit=667,file='./fftw_output_norm2d_real_y=0.txt', form='formatted')
  do i = 1, N
    write(666, '(2E14.5,x)') (i-1)*dx, arr_in(1, i)
    write(667, '(2E14.5,x)') (i-1)*dx, arr_in(i, 1)
  enddo
  close(666)
  close(667)

  write(*,*) "Finished! Written results to fftw_output_norm2d_real_x=0.txt and fftw_output_norm2d_real_y=0.txt"

  deallocate(arr_in, arr_out)
  call fftw_destroy_plan(plan_forward)
  call fftw_destroy_plan( plan_backward)

end program use_fftw

和python绘图工具:

#!/usr/bin/python3

#====================================
# Plots the results of the FFTW
# example programs.
#====================================

import numpy as np
import matplotlib.pyplot as plt
from sys import argv
from time import sleep

errormessage="""
I require an argument: Which output file to plot.
Usage: ./plot_fftw.py <case>
options for case:
    1   fftw_output_norm1d_fft.txt
    2   fftw_output_norm1d_real.txt
    3   fftw_output_norm2d_fft_x=0.txt
    4   fftw_output_norm2d_real_x=0.txt
    5   fftw_output_norm2d_fft_y=0.txt
    6   fftw_output_norm2d_real_y=0.txt


Please select a case: """

#----------------------
# Hardcoded stuff
#----------------------

file_dict={}
file_dict['1'] = ('fftw_output_norm1d_fft.txt', '1d Fourier transform')
file_dict['2'] = ('fftw_output_norm1d_real.txt', '1d Full circle')
file_dict['3'] = ('fftw_output_norm2d_fft_x=0.txt', '2d Fourier transform, x=0')
file_dict['4'] = ('fftw_output_norm2d_real_x=0.txt', '2d Full circle, x=0')
file_dict['5'] = ('fftw_output_norm2d_fft_y=0.txt', '2d Fourier transform, y=0')
file_dict['6'] = ('fftw_output_norm2d_real_y=0.txt', '2d Full circle, y=0')


#------------------------
# Get case from cmdline
#------------------------

case = ''

def enforce_integer():
    global case
    while True:
        case = input(errormessage)
        try:
            int(case)
            break
        except ValueError:
            print("\n\n!!! Error: Case must be an integer !!!\n\n")
            sleep(2)

if len(argv) != 2:
    enforce_integer()
else:
    try:
        int(argv[1])
        case = argv[1]
    except ValueError:
        enforce_integer()

filename,title=file_dict[case]


#-------------------------------
# Read and plot data
#-------------------------------

k, Pk = np.loadtxt(filename, dtype=float, unpack=True)

fig = plt.figure()

ax = fig.add_subplot(111)
#  ax.plot(k, Pk, label='power spectrum')
if case in ['1', '3', '5']:
    ax.plot(k, Pk, label='recovered wave', lw=3) # ignore negative k
    x = np.linspace(k.min(), k.max(), 1000)
    if case=='1':
        ax.plot(x, np.sin(2*np.pi*x), ':', label='expected wave', lw=3)
    if case in ['3', '5']:
        ax.plot(x, np.sin(x), ':', label='expected wave', lw=3)
    ax.set_title(title)
    ax.set_xlabel("k")
    ax.set_ylabel("F(k)")

if case in ['2', '4', '6']:
    # in this case: k=x, Pk=f(x)
    ax.plot(k, Pk, label='recovered original', lw=3) # ignore negative k
    N=1000
    plen=500
    dx=plen/N
    x = np.linspace(k.min(), k.max(), 1000)
    y = np.zeros(1000)
    ind = int(1.0/dx)

    if case=='2':
        y[ind] = -0.5
        y[-ind] = 0.5
    if case in ['4', '6']:
        y[ind] = np.sqrt(np.pi/2)
        y[-ind] = -np.sqrt(np.pi/2)

    ax.plot(x, y, ':', label='expected original', lw=3)
    ax.set_title(title)
    ax.set_xlabel("x")
    ax.set_ylabel("f(x)")

ax.legend()

plt.show()

0 个答案:

没有答案