开发者

How to calculate wavenumber domain coordinates from a 2d FFT

开发者 https://www.devze.com 2023-03-30 06:13 出处:网络
I have a 2d Array of complex numbers that represent a potential field measured along a plane in real space. Lets say that the array is 128 cells by 128 cells and the the total area of the plane is 500

I have a 2d Array of complex numbers that represent a potential field measured along a plane in real space. Lets say that the array is 128 cells by 128 cells and the the total area of the plane is 500m x 500m. Each cell in this array represents a point in the spatial domain with co-ordinates given in x and y.

When I use the 2d FFT from scipy.fftpack on this 2d array I get the same information represented in the wave domain. How do I calcu开发者_JS百科late the wave domain co-ordinates kx and ky for the points the output array?


Here is some code to fully demonstrate the problem and the solution I was able to find.

from numpy import linspace , arange , reshape ,zeros
from scipy.fftpack import fft2 , fftfreq
from cmath import pi

# create some arbitrary data
some_data = arange(0.0 , 16384.0 , dtype = complex)

# reshape it to be a 128x128 2d grid
some_data_grid = reshape(some_data , (128 , 128) )

# assign some real spatial co-ordinates to the grid points   
# first define the edge values
x_min = -250.0
x_max = 250.0
y_min = -250.0
y_max = 250

# then create some empty 2d arrays to hold the individual cell values
x_array = zeros( (128,128) , dtype = float )
y_array = zeros( (128,128) , dtype = float )

# now fill the arrays with the associated values
for row , y_value in enumerate(linspace (y_min , y_max , num = 128) ):

  for column , x_value in enumerate(linspace (x_min , x_max , num = 128) ):

    x_array[row][column] = x_value
    y_array[row][column] = y_value

# now for any row,column pair the x_array and y_array hold the spatial domain
# co-ordinates of the associated point in some_data_grid

# now use the fft to transform the data to the wavenumber domain
some_data_wavedomain = fft2(some_data_grid)

# now we can use fftfreq to give us a base for the wavenumber co-ords
# this returns [0.0 , 1.0 , 2.0 , ... , 62.0 , 63.0 , -64.0 , -63.0 , ... , -2.0 , -1.0 ]
n_value = fftfreq( 128 , (1.0 / 128.0 ) )

# now we can initialize some arrays to hold the wavenumber co-ordinates of each cell
kx_array = zeros( (128,128) , dtype = float )
ky_array = zeros( (128,128) , dtype = float )

# before we can calculate the wavenumbers we need to know the total length of the spatial
# domain data in x and y. This assumes that the spatial domain units are metres and
# will result in wavenumber domain units of radians / metre.
x_length = x_max - x_min
y_length = y_max - y_min

# now the loops to calculate the wavenumbers
for row in xrange(128):

  for column in xrange(128):

    kx_array[row][column] = ( 2.0 * pi * n_value[column] ) / x_length
    ky_array[row][column] = ( 2.0 * pi * n_value[row] ) / y_length

# now for any row,column pair kx_array , and ky_array will hold the wavedomain coordinates
# of the correspoing point in some_data_wavedomain

I know this is likely not the most efficient way of doing this, but hopefully it is easy to understand. I hope this helps someone avoid a little frustration.


Hmm, it seems if I use the FFT function, that DC ends up at element zero, also in your case the spacing between frequencies is 1/500m. Thus the following (not very concise) snippet will get your frequency axis:

meter = 1.0
L     = 500.0 * meter
N     = 128

dF    = 1.0 / L
freqs = arange(0, N/L, dF)  # array of spatial frequencies.

Naturally these frequencies are in cycles-per-meter, not radians-per-meter. If I wanted kx and ky to be arrays of spatial frequencies in rads/meter I would just say

 kx = 2*pi*freqs
 ky = 2*pi*freqs

(Assuming I have imported in things like arange, and pi already).

edit

Stu makes a good point about frequencies above the Nyquist, which you might prefer to think of as negative (I usually do, but not in the code). You can always do:

freqs[freqs > 0.5*N/(2*L)] -= N/L

But if you really want negative frequencies you might also want to play with fftshift, as well -- another can of worms.


I found subroutine in FORTRAN code below, and try to implemented it as matlab function. Please give me a comment whether my translation is right. Here we Go, This is FORTRAN subroutine :

subroutine kvalue(i,j,nx,ny,dkx,dky,kx,ky)
  c Subroutine KVALUE finds the wavenumber coordinates of one
  c element of a rectangular grid from subroutine FOURN.
  c
  c Input parameters:
  c i - index in the ky direction,
  c j - index in the kx direction.
  c nx - dimension of grid in ky direction (a power of two).
  c ny - dimension of grid in kx direction (a power of two).
  c dkx - sample interval in the kx direction,
  c dky - sample interval in the ky direction,
  c
  c Output parameters:
  c kx - the wavenumber coordinate in the kx direction,
  c ky - the wavenumber coordinate in the ky direction,
  c
  real kx,ky
  nyqx=nx/2+l
  nyqy=ny/2+l
  if(j.le.nyqx)then
      kx=(j-l)*dkx
  else
      kx=(j-nx-l)*dkx
  end if
  if(i.le.nyqy)then
          ky=(i-l)*dky
  else
          ky=(i-ny-l)*dky
  end if
  return
  end

I translate to MATLAB function :

function [kx,ky]=kvalue(gz,nx,ny,dkx,dky)
nyq_x=nx/2+1;
nyq_y=ny/2+1;
for i=1:length(gz)
    for j=1:length(gz)
        if j <= nyq_x
            kx(j)=(j-1)*dkx;
        else
            kx(j)=(j-nx-1)*dkx;
        end
        if i <= nyq_y
            ky(i)=(i-1)*dky;
        else
            ky(i)=(i-ny-1)*dky;
        end
    end
end

Thank you.

0

精彩评论

暂无评论...
验证码 换一张
取 消