Aligning and translating a beam with steering mirrors¶
This example works out how a beam can be shifted off-axis in Finesse using a telescope such as depicted in Fig. 3, and verifies the analytical results derived in Simulating off-axis beams: calculating the shift with simulations in Finesse.
Introduction¶
Given a Z-shaped telescope setup with two beamsplitters such as depicted in Fig. 3, we apply a small additional tilt \(\beta\) to the angle of incidence \(\alpha\) of both beamsplitters. This will cause the outgoing beam to shift over a small distance \(\Delta\) with respect to the untilted setup.
Such a small extra tilt is applied in Finesse via the xbeta
parameter of a
bs
component. With respect to the “untilted beam” (i.e.
\(\beta\) = 0) we derive in section Simulating off-axis beams: calculating the shift both the shift in its
position \(\Delta\) and the change of its phase \(\delta\varphi\), each
as a function of the angle \(\beta\) and the distance \(s\) between the
beamsplitters. For the shift \(\Delta\) we find there Eq.
(10):
while for the phase change \(\delta\varphi\) we derive Eq. (18):
Basic setup¶
Our basic setup consists of:
a
laser
with power 1.5 Watttwo
beamsplitter
components, bs1 and bs2:the first 1.0 km right of the laser, the second 400 meters away from the first.
each having an angle of incidence \(\alpha = 30^\circ\).
each tilted over a small angle \(\beta = 10^{-5}\) radians.
a Gaussian beam with waist-size \(w_0 = 10\) mm, at a distance 1.2 km right of the laser, i.e. precisely in the middle between the two beamsplitters.
we do most calculations using maxtem 7 (i.e. \(n+m\) of the higher order modes is \(\le 7\))
furthermore, the reference wavelength of Finesse is \(\lambda = 1064\) nm.
For these parameters Eq. (1) gives the following value for dimensionless \(\Delta\)
while Eq. (2) gives for \(\delta\varphi\)
Our basic Finesse setup looks as follows
import numpy as np
import matplotlib.pyplot as plt
import finesse
finesse.configure(plotting=True)
power = 1.5 # default laser power in Watt
w0 = 10e-3 # default gaussian waist size in meter3
alpha = 30 # angle of incidence for both BS in degrees0
xbeta = 1e-5 # default tilt of both beam splitters in radians
s1 = 1000 # distance till 1st beamsplitter
s2 = 400 # distance between beamsplitters
s3 = 600 # default distance between 2nd beamsplitter and detector
z0 = -1200 # default waist position, 1200 meter right of laser
maxtem = 7 # default maxtem
basescript = f"""
laser l1 P={power}
gauss g1 l1.p1.o w0={w0} z={z0}
modes(maxtem={maxtem})
space s1 l1.p1 bs1.p1 L={s1}
beamsplitter bs1 R=1 T=0 alpha={alpha} xbeta={xbeta}
space s2 bs1.p2 bs2.p1 L={s2}
beamsplitter bs2 R=1 T=0 alpha=bs1.alpha xbeta=bs1.xbeta
space s3 bs2.p2 n1.p1 L={s3}
# Add a 'nothing' component at the location of the detector
nothing n1
"""
basekat = finesse.Model()
basekat.parse(basescript)
Measuring a parallel beam¶
We will use our experimental setup in combination with a 1-dimensional
ccdline
to measure the beam shape after 600 and 1000 meters
respectively.
# Simulations: measure beam 600 and 1000 meter after second BS
s3a = 600
s3b = 1000
kat1 = finesse.Model()
kat1.parse(basescript)
script1 = f"""
# Set a 1-D CCD at n1, measure around peak at 0.8 at 600 and 1000m
ccdline ccd1 node=n1.p1.i xlim=[0.5,1.0] npts=200
# Also put a full 2-D CCD at the same position
ccd ccd2 node=n1.p1.i xlim=[-3,3] ylim=[-3,3] npts=200
series(
noxaxis(name="S600"),
change(s3.L={s3b}),
noxaxis(name="S1000")
)
"""
kat1.parse(script1)
out1 = kat1.run()
We have also added a ccd
of which we will use the output
below to plot the 2-dimensional beam cross section.
Plotting the output from the two 1-dimensional ccdline
detectors
# Plot the results
f,ax = plt.subplots(ncols=2, figsize=(12, 5))
for (i, name, s3val) in ([0, 'S600', s3a], [1, 'S1000', s3b]):
ax[i].plot(kat1.ccd1.xdata, out1[name]['ccd1'],'r')
ax[i].set_title(f"{s3val:.0f} meter")
ax[i].set_xlabel("x/w0")
ax[i].set_ylabel("intensity")
we see that the Gaussian profile over distance becomes lower and wider, but retains its peak at the expected position \(x/w0 = 0.8\), confirming we are simulating a parallel but shifted beam.
For reference we also plot the 2-dimensional beam cross section at 600 meters as
measured with the ccd
:
f,ax = plt.subplots(ncols=1, figsize=(5, 5))
pxy_extent = (kat1.ccd2.xdata.min(), kat1.ccd2.xdata.max(),
kat1.ccd2.ydata.min(), kat1.ccd2.ydata.max())
ax.imshow(out1['S600']['ccd2'].T, aspect='auto', extent = pxy_extent)
ax.set_xlabel("x/w0")
ax.set_ylabel("y/w0");
Measuring phase shift across the beam¶
We next measure the phase shift of the outgoing beam resulting from the tilting
of the beamsplitters. For this we use the following simulation script (again in
addition to the basic experimental setup), using a
fline
detector measuring the phase along the beam cross section
at a distance 600 meter after the second beamsplitter.
For reference, we also run the same simulation for \(\beta=0\).
We expect the full result to be shifted over \(0.8 w_0\) and to have a phase
that is larger by about \(27.1^\circ\).
# Plotting range for x: [peak-dx...peak+dx]
dx = 0.8
# Calculate expected Delta and delta phi
Delta = s2*np.sin(2*xbeta)/w0
dphi = 2*s2*np.sin(xbeta)**2/basekat.lambda0*360
kat2 = finesse.Model()
kat2.parse(basescript)
script2 = f"""
fline fl1 node=n1.p1.i xlim=[{Delta}-{dx},{Delta}+{dx}] npts=200
fline fl2 node=n1.p1.i xlim=[-{dx},{dx}] npts=40
series(
noxaxis(name='full'),
change(bs1.xbeta=0),
noxaxis(name='ref')
)
"""
kat2.parse(script2)
out2 = kat2.run()
We expect the phase for the shifted beam to satisfy
In the plot below we combine the result for the full simulation (red drawn line) with this expected result (blue points), obtained from translating the \(\beta=0\) curve. It shows that the simulation matches the prediction perfectly.
f,ax = plt.subplots(ncols=1, figsize=(8, 5))
ax.plot(kat2.fl1.xdata,
np.angle(out2['full']['fl1'], deg=True),
'r', label="measured")
ax.plot(kat2.fl2.xdata + Delta,
np.angle(out2['ref']['fl2'], deg=True) + dphi,
'bp', label="prediction")
ax.set_title("xbeta=1e-5 radian, with predicted peak")
ax.set_xlabel("x/w0")
ax.set_ylabel("phase (°)")
ax.legend(loc='lower center');
Measuring phase shift versus tilt angle¶
The expected phase dependence as a function of the tilt angle \(\beta\) is
given by Eq. (2), which we can verify using an ad
detector. We will use the following script (again in addition to the basic
experimental setup)
kat3 = finesse.Model()
kat3.parse(basescript)
script3 = f"""
# Note: important to specify n,m
amplitude_detector ad1 node=n1.p1.i f=0 n=0 m=0
xaxis(bs1.xbeta, lin, -1.5e-5, 1.5e-5, 40)
"""
kat3.parse(script3)
out3 = kat3.run()
and plot the result together with the expected behaviour Eq. (2).
Note that we now have to grab the horizontal data from the ArraySolution
object (out3) instead of from the Model
object (kat3) since for
this ad
detector we use an xaxis
instead of a
noxaxis
action. Again we see that the simulation matches
perfectly with the prediction.
f,ax = plt.subplots(ncols=1, figsize=(8, 5))
ax.plot(out3.x[0], np.angle(out3['ad1'], deg=True),
'r', label="measured")
ax.plot(out3.x[0], 2*s2*np.sin(out3.x[0])**2/basekat.lambda0*360,
'bp', label="2s·sin²(β)/λ·360°")
ax.set_title("measured versus calculated")
ax.set_xlabel("xbeta (radian)")
ax.set_ylabel("phase (°)")
ax.legend(loc="upper center");