The Fourier transform of an intensity map can expose real-space length scales that underlie a scattering pattern. An FFT requires values on an equidistant grid in the coordinate conjugate to the requested output. A spherical detector is equidistant in exit angles, not in scattering-vector coordinates. First create the equidistant $q_y,q_z$ approximation described in Axis coordinates and transformations:
transformation = ba.FrameTrafo.ScatteringToQ(wavelength, alpha_i)
q_result = transformation.transformedDatafield(angular_result)
Applying an FFT directly to the angular result is mathematically possible, but its conjugate axes are not real-space lengths, and the nonlinear angle-to-q relation distorts inferred periods. For real-space interpretation, use the reciprocal-space result. NumPy then provides the transform:
intensity = q_result.intensities()
fourier_values = np.fft.fft2(intensity)
fourier_magnitude = np.abs(np.fft.fftshift(fourier_values))
The conjugate coordinate values follow from the reciprocal-space bin widths. NumPy uses cycles in its frequency coordinates, whereas the scattering vector enters the Fourier phase as $q\cdot r$. Therefore the spatial coordinates contain a factor of $2\pi$:
q_y_step = np.diff(q_result.xCenters()).mean()
q_z_step = np.diff(q_result.yCenters()).mean()
n_z, n_y = intensity.shape
y = 2*np.pi*np.fft.fftshift(np.fft.fftfreq(n_y, d=q_y_step))
z = 2*np.pi*np.fft.fftshift(np.fft.fftfreq(n_z, d=q_z_step))
Attach these coordinates to the Fourier magnitude to retain Datafield
plotting and cropping operations:
real_space_frame = ba.Frame(
ba.ListScan("y (nm)", y.tolist()),
ba.ListScan("z (nm)", z.tolist()))
fourier_result = ba.Datafield(
real_space_frame, fourier_magnitude.ravel().tolist())
The complete example below simulates scattering from a square lattice, converts the angular detector axes to $q_y,q_z$, and plots the Fourier magnitude on real-space axes. The maxima repeat with the lattice period. The result is an autocorrelation-like map, not a direct reconstruction of the sample: scattering intensities contain no phase information, and the finite detector window can introduce artifacts.
Both heatmaps in the example use the default logarithmic color normalization. Changing to a linear normalization would only change the display; the plotted quantity would still be Fourier magnitude, not scattering-length density.
In the example, the complete fourier_result is kept; only a separate
fourier_crop is plotted. Its horizontal range spans six lattice periods on
each side of zero, and its more compact vertical range spans three. These
limits show several repeats clearly in the stacked layout. They can be changed
or the crop can be removed without recomputing the transform.
|
|