Example: Effect of wavefront aberrations in atom interferometry

As an example, we reproduce two plots from the paper https://link.springer.com/article/10.1007/s00340-015-6138-5.

The simulation will require the following objects and parameters * Wavefront: contains the wavefront aberrations of the interferometry lasers * AtomicEnsemble: an ensemble of atoms which different trajectories or phase space vectors * Detector: determines which atoms contribute to the signal * times of the three interferometer pulses * effective wavevector

[1]:
%matplotlib inline

import numpy as np
import matplotlib.pyplot as plt

import aisim as ais

Loading and preparing wavefront data

Wavefront aberration in multiples of \(\lambda\) = 780 nm.

Load Zernike coefficients from file:

[2]:
coeff_window = np.loadtxt('data/wf_window.txt')

Creating Wavefront objects and removing piston, tip and tilt from the data:

[3]:
r_beam = 11e-3 # radius of the available wavefront data in m

wf = ais.Wavefront(r_beam, coeff_window)
for n in [0,1,2]:
    wf.coeff[n] = 0
[4]:
wf.plot()
fig, ax = wf.plot_coeff()
../_images/examples_wavefront-aberrations_8_0.png
../_images/examples_wavefront-aberrations_8_1.png

Creating an atomic ensemble

Due to the large number of parameters determining an atomic ensemble, dictionaries are used:

[5]:
pos_params = {
    'std_rho' : 3.0e-3, # cloud radius in m
    'std_z' : 0,        # ignore z dimension, its not relevant here
    'n_rho' : 20,       # within each standard deviation of the distribution we use 20 points
    'n_theta' : 36,     # using a resolution of 10°
    'n_z' : 1,          # use one value for the distribution along z
    'm_std_rho' : 3,    # use 3 standard deviations of the distribution, i.e. atoms up to 9 mm away from the center
    'm_std_z' : 0,      # ignore z dimension, its not relevant here
}

vel_params = {
    'std_rho' : ais.vel_from_temp(3e-6), # velocity spread in m/s from a temperature of 3 uK
    'std_z' : 0,                         # ignore z dimension, its not relevant here
    'n_rho' : 20,                        # within each standard deviation of the distribution we use 20 points
    'n_theta' : 36,                      # using a resolution of 10°
    'n_z' : 1,                           # use one value for the distribution along z
    'm_std_rho' : 3,                     # use 3 standard deviations of the distribution
    'm_std_z' : 0,                       # ignore z dimension, its not relevant here
}

atoms = ais.create_ensemble_from_grids(pos_params, vel_params)

Plotting the grid and the weights:

[6]:
x = atoms.initial_position[:, 0]
y = atoms.initial_position[:, 1]
[7]:
fig, ax = plt.subplots()
ax.scatter(1e3*x, 1e3*y)
ax.set_aspect('equal', 'box')
ax.set_xlabel('x / mm')
ax.set_ylabel('y / mm')
[7]:
Text(0, 0.5, 'y / mm')
../_images/examples_wavefront-aberrations_13_1.png
[8]:
fig, ax = plt.subplots()
ax.scatter(1e3*x, atoms.weights)
ax.set_xlabel('x / mm')
ax.set_ylabel('weights')
[8]:
Text(0, 0.5, 'weights')
../_images/examples_wavefront-aberrations_14_1.png

Setting up the detector

We want to calculate the dependency of the phase shift caused by wavefront aberrations on the detection area. For this reason, we set up a Detector with varying detection radius within a for-loop.

[9]:
t_det = 778e-3 # time of the detection in s

Simulation the bias in gravity from wavefront aberrations

For the simulation we need the objects created above and the timing of the interferometer sequence.

[10]:
T = 260e-3 # interferometer time in s
t1 = 130e-3 # time of first pulse in s
t2 = t1 + T
t3 = t2 + T
[11]:
awfs = []
r_dets = np.linspace(2e-3, 10e-3, 10)
for r_det in r_dets:
    # creating detector with new detection radius
    det = ais.PolarDetector(t_det, r_det=r_det)

    det_atoms = det.detected_atoms(atoms)

    # calculate the imprinted phase for each "test atom" at each pulse. This is the computationally heavy part
    phi1 = 2 * np.pi * wf.get_value(det_atoms.calc_position(t1))
    phi2 = 2 * np.pi * wf.get_value(det_atoms.calc_position(t2))
    phi3 = 2 * np.pi * wf.get_value(det_atoms.calc_position(t3))

    # calculate a complex amplitude factor for the Mach-Zehnder sequence and
    # weight their contribution to the signal
    awf = np.exp(1j * (phi1 - 2*phi2 + phi3))
    weighted_awf = np.sum(det_atoms.weights * awf) / np.sum(det_atoms.weights)

    awfs.append(weighted_awf)

g = ais.phase_to_grav(np.angle(awfs), T=260e-3, keff=1.610574779769e6)

We load the measured gravity data from a file and compare it to the simulation results.

[12]:
data = np.loadtxt('data/wf_grav_data.csv', skiprows=1, delimiter=',')
r_det_data = data[:, 0]
grav = data[:, 1]
graverr = data[:, 2]
[13]:
fig, ax = plt.subplots()
ax.plot(1e3*r_dets, 1e9*g, label='Simulation')
ax.errorbar(1e3*r_det_data, 1e9*grav, yerr=1e9*graverr, fmt='o', label='data')
ax.set_xlabel('Detection radius / mm')
ax.set_ylabel('Gravity bias / $nm/s^2$');
ax.set_ylim([-35, -5])
ax.legend()
[13]:
<matplotlib.legend.Legend at 0x1fe648515c8>
../_images/examples_wavefront-aberrations_22_1.png

Note that a different simulation software with different simulation parameters was used in the aformentioned paper, so some differences are to be expected.