Skip to content

Commit 3a568ad

Browse files
committed
Add more experiments
1 parent 45f819d commit 3a568ad

File tree

288 files changed

+73221
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

288 files changed

+73221
-0
lines changed
Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
"""
2+
Experiment 251: Atom Interferometer Phase Shift
3+
4+
This example demonstrates atom interferometry using light-pulse techniques.
5+
We explore the phase shifts induced by various physical effects and show
6+
how atom interferometers measure acceleration, rotation, and gravity. Topics:
7+
- Mach-Zehnder atom interferometer geometry
8+
- Phase accumulation from inertial effects
9+
- Gravity measurement (gravimeter)
10+
- Sagnac effect for rotation sensing
11+
12+
Key physics:
13+
- Beam splitter: pi/2 pulse transfers superposition of momentum states
14+
- Mirror: pi pulse swaps momentum states
15+
- Free evolution: momentum states separate and accumulate phase
16+
- Phase shift from acceleration: phi = k_eff * a * T^2
17+
"""
18+
19+
import sys
20+
import os
21+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
22+
23+
import numpy as np
24+
import matplotlib.pyplot as plt
25+
from src.sciforge.physics.amo import HBAR, KB, C, M_PROTON
26+
27+
def simulate_atom_interferometer():
28+
"""Simulate Mach-Zehnder atom interferometer."""
29+
30+
# Rubidium-87 parameters
31+
wavelength = 780e-9 # m
32+
k = 2 * np.pi / wavelength # Wave vector
33+
k_eff = 2 * k # Effective wave vector for two-photon transition
34+
m_Rb = 87 * M_PROTON # Rb-87 mass
35+
v_recoil = HBAR * k / m_Rb # Recoil velocity
36+
37+
results = {}
38+
results['k_eff'] = k_eff
39+
results['m'] = m_Rb
40+
results['v_recoil'] = v_recoil
41+
results['wavelength'] = wavelength
42+
43+
print(f"Effective wave vector k_eff: {k_eff:.2e} m^-1")
44+
print(f"Recoil velocity: {v_recoil*1e3:.3f} mm/s")
45+
46+
# 1. Interferometer geometry
47+
print("\nSimulating Mach-Zehnder geometry...")
48+
49+
# Time between pulses
50+
T = 100e-3 # 100 ms interrogation time (typical for gravimeters)
51+
g = 9.81 # m/s^2
52+
53+
# Phase shift from gravity
54+
phi_g = k_eff * g * T**2
55+
print(f"Gravitational phase shift: {phi_g:.2f} rad = {phi_g/(2*np.pi):.2f} fringes")
56+
57+
results['T'] = T
58+
results['g'] = g
59+
results['phi_g'] = phi_g
60+
61+
# Trajectory calculation
62+
t_total = 2 * T
63+
t = np.linspace(0, t_total, 1000)
64+
65+
# Upper arm: receives +hbar*k_eff at t=0 (after pi/2 pulse)
66+
# Lower arm: stays in original momentum state
67+
68+
# Initial position z0=0, initial velocity v0=1 m/s (upward launch)
69+
v0 = 2.0 # m/s upward
70+
z0 = 0
71+
72+
# Lower arm trajectory (free fall, no recoil)
73+
z_lower = z0 + v0 * t - 0.5 * g * t**2
74+
75+
# Upper arm trajectory (receives recoil kick at t=0)
76+
z_upper = z0 + (v0 + v_recoil) * t - 0.5 * g * t**2
77+
78+
# At t=T (pi pulse), arms swap momentum
79+
# After pi pulse, upper arm now has lower momentum
80+
t_pi = T
81+
idx_pi = np.argmin(np.abs(t - t_pi))
82+
83+
z_upper_after = np.zeros_like(t)
84+
z_lower_after = np.zeros_like(t)
85+
z_upper_after[:idx_pi+1] = z_upper[:idx_pi+1]
86+
z_lower_after[:idx_pi+1] = z_lower[:idx_pi+1]
87+
88+
# After pi pulse: swap trajectories (with continuity)
89+
z_at_pi_upper = z_upper[idx_pi]
90+
z_at_pi_lower = z_lower[idx_pi]
91+
v_upper_before = v0 + v_recoil - g * t_pi
92+
v_lower_before = v0 - g * t_pi
93+
94+
# After swap: upper arm now moves with lower velocity, lower moves with upper
95+
for i in range(idx_pi + 1, len(t)):
96+
dt = t[i] - t_pi
97+
z_upper_after[i] = z_at_pi_upper + v_lower_before * dt - 0.5 * g * dt**2
98+
z_lower_after[i] = z_at_pi_lower + v_upper_before * dt - 0.5 * g * dt**2
99+
100+
results['trajectories'] = {
101+
't': t,
102+
'z_upper': np.where(t <= t_pi, z_upper, z_upper_after),
103+
'z_lower': np.where(t <= t_pi, z_lower, z_lower_after),
104+
't_pi': t_pi
105+
}
106+
107+
# 2. Phase shift vs interrogation time
108+
print("Computing phase shift vs interrogation time...")
109+
T_range = np.linspace(10e-3, 200e-3, 100)
110+
phi_vs_T = k_eff * g * T_range**2
111+
112+
results['phase_vs_T'] = {
113+
'T': T_range,
114+
'phi': phi_vs_T
115+
}
116+
117+
# 3. Sensitivity calculation
118+
# Shot noise limited sensitivity: delta_g / g = 1 / (k_eff * g * T^2 * sqrt(N))
119+
N_atoms = 1e6 # Number of atoms per shot
120+
cycle_time = 1.0 # 1 second per measurement
121+
122+
delta_phi = 1 / np.sqrt(N_atoms) # Phase sensitivity
123+
delta_g = delta_phi / (k_eff * T_range**2) # Gravity sensitivity
124+
125+
results['sensitivity'] = {
126+
'T': T_range,
127+
'delta_g': delta_g,
128+
'N_atoms': N_atoms
129+
}
130+
131+
# 4. Interferometer fringes
132+
print("Computing interference fringes...")
133+
134+
# Scan applied phase (e.g., by varying laser phase)
135+
phi_scan = np.linspace(0, 4 * np.pi, 200)
136+
137+
# At output, probability of being in upper state:
138+
# P = (1 + cos(phi_total)) / 2
139+
# where phi_total = phi_g + phi_applied
140+
141+
P_output = (1 + np.cos(phi_g + phi_scan)) / 2
142+
143+
results['fringes'] = {
144+
'phi_scan': phi_scan,
145+
'P': P_output
146+
}
147+
148+
# 5. Sagnac phase for rotation sensing
149+
print("Computing Sagnac effect...")
150+
151+
# Sagnac phase: phi_Sagnac = 4 * m * A * Omega / hbar
152+
# where A is enclosed area, Omega is rotation rate
153+
154+
A_enclosed = 0.01 # m^2 (10 cm x 10 cm effective area)
155+
Omega_range = np.linspace(0, 1e-5, 100) # Earth rotation ~ 7e-5 rad/s
156+
157+
phi_Sagnac = 4 * m_Rb * A_enclosed * Omega_range / HBAR
158+
159+
results['sagnac'] = {
160+
'Omega': Omega_range,
161+
'phi': phi_Sagnac,
162+
'A': A_enclosed
163+
}
164+
165+
# 6. Gravity gradient measurement (gradiometer)
166+
print("Computing gravity gradient sensitivity...")
167+
168+
# Two interferometers separated by baseline L
169+
L_baseline = 1.0 # m
170+
T_grad = 100e-3 # interrogation time
171+
172+
# Gravity gradient measurement
173+
# Delta_phi = k_eff * grad_g * L * T^2
174+
grad_g_range = np.linspace(0, 3e-6, 100) # s^-2 (typical ~ 3e-6)
175+
176+
delta_phi_grad = k_eff * grad_g_range * L_baseline * T_grad**2
177+
178+
results['gradiometer'] = {
179+
'grad_g': grad_g_range,
180+
'delta_phi': delta_phi_grad,
181+
'L': L_baseline,
182+
'T': T_grad
183+
}
184+
185+
return results
186+
187+
188+
def plot_results(results):
189+
"""Create comprehensive visualization of atom interferometry."""
190+
191+
fig = plt.figure(figsize=(14, 12))
192+
193+
# Plot 1: Mach-Zehnder trajectories
194+
ax1 = fig.add_subplot(2, 2, 1)
195+
traj = results['trajectories']
196+
197+
ax1.plot(traj['t'] * 1e3, traj['z_upper'] * 1e3, 'b-', linewidth=2,
198+
label='Upper arm')
199+
ax1.plot(traj['t'] * 1e3, traj['z_lower'] * 1e3, 'r-', linewidth=2,
200+
label='Lower arm')
201+
202+
# Mark pulse positions
203+
T = results['T']
204+
ax1.axvline(x=0, color='green', linestyle='--', alpha=0.7, label=r'$\pi/2$ pulse')
205+
ax1.axvline(x=T * 1e3, color='purple', linestyle='--', alpha=0.7, label=r'$\pi$ pulse')
206+
ax1.axvline(x=2 * T * 1e3, color='green', linestyle='--', alpha=0.7)
207+
208+
ax1.set_xlabel('Time (ms)', fontsize=11)
209+
ax1.set_ylabel('Position (mm)', fontsize=11)
210+
ax1.set_title('Mach-Zehnder Atom Interferometer Trajectories', fontsize=12)
211+
ax1.legend(loc='upper right', fontsize=9)
212+
ax1.grid(True, alpha=0.3)
213+
214+
# Add annotation about momentum states
215+
ax1.annotate(r'$|p\rangle + |p+\hbar k\rangle$', xy=(T*1e3/2, 100),
216+
fontsize=9, ha='center',
217+
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
218+
219+
# Plot 2: Interference fringes
220+
ax2 = fig.add_subplot(2, 2, 2)
221+
fr = results['fringes']
222+
223+
ax2.plot(fr['phi_scan'] / np.pi, fr['P'], 'b-', linewidth=2)
224+
ax2.axhline(y=0.5, color='gray', linestyle=':', alpha=0.5)
225+
226+
# Mark gravitational phase
227+
phi_g = results['phi_g']
228+
ax2.axvline(x=phi_g / np.pi, color='red', linestyle='--', alpha=0.7,
229+
label=f'Gravity: {phi_g/(2*np.pi):.1f} fringes')
230+
231+
ax2.set_xlabel(r'Applied Phase ($\pi$ rad)', fontsize=11)
232+
ax2.set_ylabel('Output Probability', fontsize=11)
233+
ax2.set_title(f'Interference Fringes (T = {T*1e3:.0f} ms)', fontsize=12)
234+
ax2.legend(loc='upper right', fontsize=9)
235+
ax2.set_xlim(0, 4)
236+
ax2.grid(True, alpha=0.3)
237+
238+
# Add inset showing fringe contrast
239+
ax2.text(0.05, 0.05, 'Fringe contrast ~ 100%\n(ideal case)',
240+
transform=ax2.transAxes, fontsize=9,
241+
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
242+
243+
# Plot 3: Phase shift and sensitivity vs T
244+
ax3 = fig.add_subplot(2, 2, 3)
245+
pv = results['phase_vs_T']
246+
sv = results['sensitivity']
247+
248+
ax3_twin = ax3.twinx()
249+
250+
l1, = ax3.plot(pv['T'] * 1e3, pv['phi'] / (2 * np.pi), 'b-', linewidth=2,
251+
label='Phase shift')
252+
l2, = ax3_twin.semilogy(sv['T'] * 1e3, sv['delta_g'] / results['g'] * 1e9, 'r-',
253+
linewidth=2, label=r'$\delta g / g$')
254+
255+
ax3.set_xlabel('Interrogation Time T (ms)', fontsize=11)
256+
ax3.set_ylabel('Phase Shift (fringes)', fontsize=11, color='b')
257+
ax3_twin.set_ylabel(r'Relative Sensitivity $\delta g/g$ (ppb)', fontsize=11, color='r')
258+
ax3.set_title('Gravimeter Phase Shift and Sensitivity', fontsize=12)
259+
260+
# Combined legend
261+
ax3.legend([l1, l2], ['Phase shift', 'Sensitivity (ppb)'],
262+
loc='upper left', fontsize=9)
263+
ax3.grid(True, alpha=0.3)
264+
ax3.tick_params(axis='y', labelcolor='b')
265+
ax3_twin.tick_params(axis='y', labelcolor='r')
266+
267+
# Plot 4: Various applications
268+
ax4 = fig.add_subplot(2, 2, 4)
269+
270+
# Sagnac effect for rotation sensing
271+
sg = results['sagnac']
272+
ax4.plot(sg['Omega'] * 1e6, sg['phi'], 'g-', linewidth=2,
273+
label=f'Gyroscope (A = {sg["A"]*1e4:.0f} cm$^2$)')
274+
275+
# Mark Earth rotation
276+
Omega_Earth = 7.27e-5 # rad/s
277+
phi_Earth = 4 * results['m'] * sg['A'] * Omega_Earth / HBAR
278+
ax4.axvline(x=Omega_Earth * 1e6, color='orange', linestyle='--', alpha=0.7)
279+
ax4.text(Omega_Earth * 1e6 * 1.1, max(sg['phi']) * 0.8,
280+
'Earth\nrotation', fontsize=8, color='orange')
281+
282+
ax4.set_xlabel(r'Rotation Rate ($\mu$rad/s)', fontsize=11)
283+
ax4.set_ylabel('Sagnac Phase (rad)', fontsize=11)
284+
ax4.set_title('Sagnac Effect for Rotation Sensing', fontsize=12)
285+
ax4.legend(loc='upper left', fontsize=9)
286+
ax4.grid(True, alpha=0.3)
287+
288+
# Add text about applications
289+
textstr = '\n'.join([
290+
'Applications:',
291+
'- Gravimeters: $\\delta g/g \\sim 10^{-9}$',
292+
'- Gyroscopes: $\\delta\\Omega \\sim nrad/s$',
293+
'- Gradiometers: gravity mapping',
294+
'- Tests of general relativity'
295+
])
296+
ax4.text(0.05, 0.05, textstr, transform=ax4.transAxes, fontsize=9,
297+
verticalalignment='bottom',
298+
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
299+
300+
plt.tight_layout()
301+
return fig
302+
303+
304+
def main():
305+
print("=" * 60)
306+
print("Experiment 251: Atom Interferometer Phase Shift")
307+
print("=" * 60)
308+
print()
309+
310+
# Run simulation
311+
print("Running simulations...")
312+
results = simulate_atom_interferometer()
313+
314+
# Create visualization
315+
print("\nCreating visualization...")
316+
fig = plot_results(results)
317+
318+
# Save figure
319+
output_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'output')
320+
os.makedirs(output_dir, exist_ok=True)
321+
output_path = os.path.join(output_dir, 'atom_interferometer.png')
322+
fig.savefig(output_path, dpi=150, bbox_inches='tight')
323+
print(f"\nPlot saved to: {output_path}")
324+
325+
# Print summary
326+
print("\n" + "=" * 60)
327+
print("Summary (Rb-87 Mach-Zehnder Interferometer):")
328+
print("=" * 60)
329+
print(f"Wavelength: {results['wavelength']*1e9:.0f} nm")
330+
print(f"Effective wave vector: {results['k_eff']:.2e} m^-1")
331+
print(f"Recoil velocity: {results['v_recoil']*1e3:.3f} mm/s")
332+
print()
333+
print(f"Interrogation time: {results['T']*1e3:.0f} ms")
334+
print(f"Gravitational phase shift: {results['phi_g']:.2f} rad = {results['phi_g']/(2*np.pi):.2f} fringes")
335+
print()
336+
print("Key phase shift formulas:")
337+
print(" Gravity: phi = k_eff * g * T^2")
338+
print(" Rotation (Sagnac): phi = 4 * m * A * Omega / hbar")
339+
print(" Gravity gradient: phi = k_eff * grad_g * L * T^2")
340+
print()
341+
print("Achievable sensitivities:")
342+
print(" - Gravity: delta_g/g ~ 10^-9 (ppb level)")
343+
print(" - Rotation: delta_Omega ~ nrad/s")
344+
print(" - Equivalent to optical interferometers at lambda ~ nm scale!")
345+
346+
plt.close()
347+
348+
349+
if __name__ == "__main__":
350+
main()

0 commit comments

Comments
 (0)