<jupyter_start><jupyter_text>Fourier transforms for reflectometry analysis Specular reflectometryIn specular neutron reflectometry, we accept that $q_x = 0$ and $q_y = 0$, which is to say that the neutrons are scattered **only** in the surface normal (perpendicular to the plane of the surface), denoted $z$. A result of this is that by applying the [Born approximation](https://en.wikipedia.org/wiki/Born_approximation), the measured reflectometry can be found from the Fourier transform of the **first derivative** of the scattering length density profile with respect to $z$ {cite}`sivia_elementary_2011`.The sensitivity to the first derivative indicates the importance of the change of the scattering length density profile. ```{figure} ../figures/fourier.png---height: 120pxalign: centername: fourier-relationship---The relationship between the scattering length density and the reflectometry profiles, interacting by a Fourier transform.```This means that the reflectometry profile, $R(q)$, can be found **under the Born approximation** as follows, $$R(q) \approx \frac{16\pi^2}{q^4} \bigg| \int^{\infty}_{-\infty} \frac{\mathrm{d}\beta}{\mathrm{d}z} \mathrm{exp}(\mathit{i}zq) \;\mathrm{d}z \;\bigg| ^2,$$where $\beta(z)$ is the scattering length density profile as a function of $z$.  The example of a single interface````{margin}```{note}This example has been borrowed from the **fantastic** book by Dr <NAME>, "Elementary Scattering Theory: For X-ray and Neutron Users" {cite}`sivia_elementary_2011`. ```````To show the Fourier transform in action, we will first consider the simple example of the interface between air and a flat piece of silicon. Let's visualise the scattering length density, $\beta$, profile of this interface.<jupyter_code>import numpy as np
import matplotlib.pyplot as plt

z = np.linspace(-20, 20, 1000)
beta = np.zeros_like(z)
beta[np.where(z > 0)] = 2.074e-6
plt.plot(z, beta)
plt.xlabel('$z$ (Å)')
plt.ylabel(r'$\beta(z)$ (Å$^{-2}$)')
plt.show()<jupyter_output><empty_output><jupyter_text>The functional description of this interface is a [Heaviside function](https://en.wikipedia.org/wiki/Heaviside_step_function) and the first derivative of this is a scaled-[delta function](https://en.wikipedia.org/wiki/Dirac_delta_function), where the scaling is the change observed in the Heaviside function. This first derivative is shown below.<jupyter_code>plt.plot(z[:-1] + np.diff(z) * 0.5, np.diff(beta))
plt.xlabel('$z$ (Å)')
plt.ylabel(r'$\mathrm{d}\beta(z)/\mathrm{d}z$ (Å$^{-3}$)')
plt.show()<jupyter_output><empty_output><jupyter_text>The scaled-delta function can be written as, $$\frac{\mathrm{d}\beta(z)}{\mathrm{d}z} = \beta_{\mathrm{Si}} \delta(z),$$where, $\beta_{\mathrm{Si}}$ is the scattering length density of silicon ($2.871\times 10^{-6}$ Å-2). We can then rewrite the reflectometry equation above as, $$R(q) \approx \frac{16\pi^2}{q^4} \bigg| \int^{\infty}_{-\infty} \beta_{\mathrm{Si}}\delta(z) \mathrm{exp}(\mathit{i}zq) \;\mathrm{d}z \;\bigg| ^2 = \frac{16\pi^2}{q^4} \bigg| \;\beta_{\mathrm{Si}} \mathrm{exp}(0) \;\bigg| ^2 = \frac{16\pi^2\beta_{\mathrm{Si}}^2}{q^4}.$$We can plot this as a function of $q$.<jupyter_code>q = np.linspace(0.001, 0.2, 500)
plt.plot(q, 16 * np.pi ** 2 * 2.074e-6 ** 2 / (q ** 4))
plt.axhline(1, c='k')
plt.xlabel('q (Å$^{-1}$)')
plt.ylabel('R(q)')
plt.yscale('log')
plt.show()<jupyter_output><empty_output><jupyter_text>The blue line in the figure above shows the reflectometry profile found using the Born approximation for a single silicon-air interface. After another example, we will investigate the meaning of the black horizontal line. An oxide layerLet's look again at a smooth sample of silicon, however, this time, a 20 Å layer of silicon dioxide has formed on top of it. The scattering length density profile for this system is shown below, where $\beta_{\mathrm{Si}} = 2.074\times 10^{-6}$ Å-2 and $\beta_{\mathrm{SiO_2}} = 4.186\times 10^{-6}$ Å-2.<jupyter_code>z = np.linspace(-20, 300, 1000)
beta = np.zeros_like(z)
beta[np.where((z > 0) & (z < 200))] = 4.186e-6
beta[np.where(z > 200)] = 2.074e-6
plt.plot(z, beta)
plt.xlabel('$z$ (Å)')
plt.ylabel(r'$\beta(z)$ (Å$^{-2}$)')
plt.show()<jupyter_output><empty_output><jupyter_text>Again, we can find the derivative of this scattering length density profile and plot this.<jupyter_code>plt.plot(z[:-1] + np.diff(z) * 0.5, np.diff(beta))
plt.xlabel('$z$ (Å)')
plt.ylabel(r'$\mathrm{d}\beta(z)/\mathrm{d}z$ (Å$^{-3}$)')
plt.show()<jupyter_output><empty_output><jupyter_text>Then, to calculate the Born approximated reflected intensity, we use the square magnitude of the Fourier transform and scale by $16 \pi^2 / q ^4$.<jupyter_code>def reflectivity(q: np.ndarray, beta:np.ndarray, z: np.ndarray) -> np.ndarray:
    """
    :param q: array of q-wavevectors
    :param beta: scattering length density profile
    :param z: z-dimension values for beta
    :returns: calculated reflectivity
    """
    fourier_transform = (np.diff(beta) * np.exp(-1j * z[:-1] * q[:, np.newaxis])).sum(-1)
    return 16 * np.pi ** 2 / (q ** 4) * np.square(np.abs(fourier_transform))<jupyter_output><empty_output><jupyter_text>The result for the oxide layer system is plotted below.<jupyter_code>q = np.linspace(0.001, 0.2, 500)
r = reflectivity(q, beta, z)
plt.plot(q, r)
plt.axhline(1, c='k')
plt.xlabel('q (Å$^{-1}$)')
plt.ylabel('R(q)')
plt.yscale('log')
plt.show()<jupyter_output><empty_output><jupyter_text>Again the blue line shows the reflectometry profile. The black line (as above) shows where $R(q) = 1$, indicating that using this approach, as $q$ tends towards $0$ the number of reflected neutrons becomes **greater than** the number of neutrons incident on the sample.This **unphysical result** is due, in part, to the utilisation of the Born approximation (this is discussed in detail on pg. 104 of "Elementary Scattering Theory: For X-ray and Neutron Users" {cite}`sivia_elementary_2011`. ````{margin}```{warning}Reflected intensity is **explicitly** defined as the number of reflected neutrons divided by the number of incident neutrons as a function of $q$. ``````` Using the Born approximationIn addition to the unphysical intensities that can be produced from a Born approximation model, the **phase problem** is present in this methodology.The [phase problem](https://en.wikipedia.org/wiki/Phase_problem) is the loss of the radiation phase on physical measurement. This means that, as we will show below, although the thickness information may be available from an inverse Fourier transform of measured data. A full analysis is **not possible** without the use of some phase retrival methodology {cite}`majkrzak_phase_1998`. ````{margin}```{note}If you have studied crystallography, you should be familiar with the **phase problem**.```````These issues do not render the Born approximation useless in reflectometry analysis. Instead, it can be used as a quick tool to **estimate** the thickness of some layered material from a reflectometry profile. We can perform an inverse Fourier transform as follows, $$\mathrm{ACF}(z) = \frac{1}{16\pi^2}\int^{\infty}_{-\infty} q^4 R(q) \exp(\mathit{i}zq) \;\mathrm{d}q, $$to obtain the **autocorrelation function** (ACF) for our data. The ACF can be thought of as a noisy version of our first derivative of the scattering length density profile.<jupyter_code>def autocorrelation(q:np.ndarray, r: np.ndarray, z:np.ndarray) -> np.ndarray:
    """
    :param q: array of q-wavevectors
    :param r: measured reflectometry
    :param z: range for z-dimension values
    :returns: normalised autocorrelation function.
    """
    inv_fourier_transform = (q ** 4 * r * np.exp(1j * q * z[:, np.newaxis])).sum(-1) 
    return inv_fourier_transform / (16 * np.pi ** 2)<jupyter_output><empty_output><jupyter_text>The noise is due to the limited $q$-range and resolution from our measurement, below we show the effect of different values of $q_{\mathrm{max}}$.<jupyter_code>q2 = np.linspace(0.001, 1, 500)
r2 = reflectivity(q2, beta, z)
acf = autocorrelation(q, r, z)
acf2 = autocorrelation(q2, r2, z)
plt.plot(z, np.abs(acf), label='$q_{\mathrm{max}} = 0.2$ Å')
plt.plot(z, np.abs(acf2), label='$q_{\mathrm{max}} = 1.0$ Å')
plt.xlabel('$z$ (Å)')
plt.ylabel('|ACF($z$)|')
plt.legend()
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.14991564232345145, 'fraction_numerical': 0.023740660400096408, 'mean_word_length': 5.216479400749064, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 43, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '346768', 'license': 'CC-BY-4.0', 'n_tokens_mistral': 2869, 'n_tokens_neox': 2658, 'n_words': 912, 'path': '1_the_fourier_transform/fourier_transforms_for_reflectometry_analysis.ipynb', 'repo_name': 'arm61/isis_school', 'size': '8306'}