Algorithms: FFT-III. Julia FFTW and FITS file libraries

Our hand-made FFT code from FFT-II is very fast (order N log_2(N) for a full Fourier transform of a length –N input), but we made no optimizations. The julia FFTW library (which uses routines from the C FFTW library) is highly optimized and very fast. I wrote our code in the previous post to emulate its input and output as closely as possible so that comparison is easy.

using FFTW
using Plots
N=256
L=5
t = range(-L, stop=L, length=N)
# -5.0:0.0392156862745098:5.0
# note 10/255=0.0392156862745098
k=1:256
shiftedks = fftshift(fftfreq(N)*N)
# -128:1:127

# our input
signal = cos(2*π*3 .* t)
FFTsignal = fftshift(fft(signal))

plot(x, f, title = "signal(t)", xlabel = "Time", ylabel = "f(t)")

plot((1/(2*L))*shiftedks, abs.(FFTsignal), title = "FFT", xlabel = "ω/2π", ylabel = "F(ω)")

The function fftshift(fftfreq(N)*N) creates a set of symmetric (about k=0) frequencies, which we did by hand, based on the size of the sample N which again must be a power of 2. We do some rescaling of the shifted k’s when we make the plots of the FFT’ed signal, which again is two nice delta functions.
Reading/parsing FITS files
Many of you will need to analyze spectra stored in FITS file formats. You can get a nice FITS file viewer “fv” that reads headers and prints out datafields, or you can use the FITSIO julia library to read out useful things in the header.
I downloaded a spectrum from the NSO historical library https://nispdata.nso.edu/ftp/FTS_cdrom/ and read its header to get the start/stop wavenumbers, and the wavenumber spacing (DELW). I also read the data axis into an array “I” (intensities) and create an array “k” so that I can print out pairs (k,I(k))

using FITSIO, VegaLite, DataFrames
f = FITS("881115R0.005")
# one HDU
header = read_header(f[1])
# stop/start wavenumbers
wstart=header["WSTART"]
wstop=header["WSTOP"]
# spacing
delw=header["DELW"]
# (wstop - wstart) / delw = NPO 
npo=header["NPO"]
# read length of the data axis of HDU
naxis1=header["NAXIS1"]
I=read(f[1])
k=[wstart+j*delw for j in 1:npo]
# 602112-element Vector{Float32}
close(f)

Some software that processes these very large spectra (this file had 602112 lines) expects the data to be stored in a binary format, which is about 20% the size of an ASCII data file, so we can print our (k,I(k)) pairs or just the I(k) values to a binary file (as binary Float32 data)

""" Write FITS file intensities to binary file"""
binwrite=function(Intensities,outfile)
  open(outfile, "w") do file
         for j in 1:length(Intensities)
         write(file, Float32.(Intensities[j])) # write a Float64
         end
  end

Once we have the (k,I(k)) pairs, we can do things like find centers of gravities of lines, search for strong/high I(k) lines, and make plots of parts of the spectrum. I find that the VegaLite plotting library works best for this

"""COG(L,R,k,I)=Center of gravity of intensities for L<= indices <=R
   k is set of wavenumbers, I is FITS intensities"""
COG=function(L,R,k,I)
sum(k[L:R,1] .* I[L:R,1])/sum(I[L:R,1])
end

"""absCOG(L,R,k,I)=Center of gravity of abs(intensities) for L<= indices <=R
   k is set of wavenumbers, I is FITS intensities"""
absCOG=function(L,R,k,I)
sum(k[L:R,1] .* abs.(I[L:R,1]))/sum(abs.(I[L:R,1]))
end

"""Display(L,R,k,I) plots intensities for L<= indices <=R
   k is set of wavenumbers, I is FITS intensities"""
Display=function(L,R,k,I)
dat=DataFrame(k=vec(k[L:R]),I=vec(I[L:R]))
dat |> @vlplot(width=1000, :line, :k, :I)
end

For example from this datafile let’s load VegaLite and plot the region around one of the high-intensity lines using my Display function for L=46700 and R=46900:

Home 2.0
error: Content is protected !!