2. Plane Waves > 1. Fabry Perot Cavity
Finesse
Author: Daniel Töyrä
The distance in frequency between two resonance peaks is called the Free Spectral Range (FSR) of a cavity. The FSR is often an important property of a cavity since it tells how well separated the resonance peaks are. The FSR is related to the width of the peak through the cavity Finesse, which are two concepts that will be handled in the upcoming two notebooks.
Recommended notebooks before you start: We recommend that you have looked through the notebook 03_Resonance.ipynb that you find in the folder 01_Fabry_Perot_cavity before you start this one, or that you have basic experience of IPython/Jupyter Notebooks, PyKat, Finesse, and resonance in a Fabry-Perot cavity. The link above only works in the HTML-version of this course.
Reading material and references:
[1] A. Freise, K. Strain, D. Brown, and C. Bond, "Interferometer Techniques for Gravitational-Wave Detection", Living Reviews in Relativity 13, 1 (2010). - Living review article (more like a book) on laser interferometry in the frequency domain for detecting gravitational waves, and FINESSE.
[2] A. Freise, D. Brown, and C. Bond, "Finesse, Frequency domain INterferomEter Simulation SoftwarE". - FINESSE-manual
[3] FINESSE syntax reference - Useful online syntax reference for FINESSE. Also available in the Finesse manual [2], but this online version is updated more often.
After this session you will...
scipy.optimize.scalar()
for finding a local maximum/minimum.First we import the PyKat and other Python packages that we need:
import numpy as np # Importing numpy
import matplotlib # For plotting
import matplotlib.pyplot as plt
from pykat import finesse # Importing the pykat.finesse package
from pykat.commands import * # Importing all packages in pykat.commands.
from IPython.display import display, HTML # Allows us to display HTML.
# Telling the notebook to make plots inline.
%matplotlib inline
# Initialises the PyKat plotting tool. Change dpi value
# to change figure sizes on your screen.
pykat.init_pykat_plotting(dpi=90)
The setup we use is the same as we used in the four previous notebooks. It consists of an input laser, and two mirror forming a cavity as we see below. To be able to measure the laser power we also need photo diodes, which in FINESSE are able to measure the power without interfering with the beam.
Now we crate our FINESSE model of the optical setup:
basekat=finesse.kat() # initialising Finesse
basekat.verbose = False
basecode = """
l laser 1 0 n0 # Laser (Power = 1 W, wavelength offset = 0)
s s1 1 n0 nc1 # Space (Length = 1 m)
## The cavity ##
m m1 0.99 0.01 0 nc1 nc2 # Mirror (R = 0.7, T = 0.3, phi = 0)
s sL 4000 nc2 nc3 # Space (Length = 4 km)
m m2 0.99999 0.00001 0 nc3 nc4 # Mirror (R = 0.8, T = 0.2, phi = 0)
"""
basekat.parseKatCode(basecode) # Parsing the FINESSE-code
In this example we will show that the FSR changes with changing macroscopic cavity length. In each run we sweep the frequency offset of the laser. We will do three runs, each with a different macroscopic cavity length.
First we add detectors and simulation instructions:
kat1 = basekat.deepcopy()
code = '''
## Detectors ##
# Photo diodes measuring DC-power
pd refl nc1 # Reflected
pd circ nc2 # Circulating
pd tran nc4 # Transmitted
## Simulation instructions ##
#xaxis laser f lin -5k 90k 100 # Sweeping the laser frequency
yaxis abs # Returns magnitude of detector outputs
noxaxis
#attr m1 Rc -3900
#attr m2 Rc 3900
maxtem off
cav myCav m1 nc2 m2 nc3
cp myCav x finesse
'''
# Parsing the FINESSE code
kat1.parseCommands(code)
kat1.verbose = True
out1 = kat1.run()
print(out1.ylabels)
print(out1['myCav_x_finesse'])
print(kat1.m1.Rc.value)
print(kat1.m2.Rc.value)
print(out1.stderr)
print(out1['circ'])
Now, we run the simulation three times, but we change the macroscopic cavity length between each run. We store the outputs in out1a
, out1b
, and out1c
.
# 1st run, original cavity length of 4 km.
out1a = kat1.run()
# 2nd run, cavity length of 6 km.
kat1.sL.L = 6000
out1b = kat1.run()
# 3rd run, cavity length of 8 km.
kat1.sL.L = 8000
out1c = kat1.run()
Finally, we plot the results:
# Run 1
fig1a = out1a.plot(xlabel='Frequency [Hz]',
ylabel='Power [W]',
title='Run 1: Cavity length = 4 km',
loc=1)
# Run 2
fig1b = out1b.plot(xlabel='Frequency [Hz]',
ylabel='Power [W]',
title='Run 2: Cavity length = 6 km',
loc=1)
# Run 3
fig1c = out1c.plot(xlabel='Frequency [Hz]',
ylabel='Power [W]',
title='Run 3: Cavity length = 8 km',
loc = 1)
We can clearly see that the peaks are closer and closer together the more we increase the cavity length. How the relationship between the FSR and the macroscopic cavity length looks like will you investigate in task 3.1. below.
Investigate how the FSR depends on the macroscopic cavity length by testing a couple of different lengths. Find the FSR of each run either manually by "zooming" in the peaks with the FINESSE command xaxis
, or by making use of the scipy.optimize
library, where I would suggest scipy.optimize.minimize_scalar()
(see hint below). Try to also work out an analytical expression for the FSR of a cavity, and compare with your simulated result.
Hint on using scipy.optimize.minimize_scalar()
:
scipy.optimize.minimize_scalar()
requires as its first argument a name of a function to be minimised, so first define a function. For example like this (not exactly in this form, you need to do a small change for your task):
def my_function(x):
kat.laser.f = x
out = kat.run()
return out['tran']
In the Finesse code you can replace the xaxis
command with noxaxis
so that a call to kat.run()
returns only one value, which is necessary for minimize_scalar()
to work.
Then you can call the scipy function to find the minimum of my_function()
as:
import scipy.optimize as opt
opt.minimize_scalar(my_function, ...)
Answer:
Investigate how the FSR depends on the macroscopic cavity length by testing a couple of different lengths. Find the FSR of each run either manually by "zooming" in the peaks with the FINESSE command xaxis
, or by making use of the scipy.optimize
library, where I would suggest scipy.optimize.minimize_scalar()
(see hint below). Try to also work out an analytical expression for the FSR of a cavity, and compare with your simulated result.
Hint on using scipy.optimize.minimize_scalar()
:
scipy.optimize.minimize_scalar()
requires as its first argument a name of a function to be minimised, so first define a function. For example like this (not exactly in this form, you need to do a small change for your task):
def my_function(x):
kat.laser.f = x
out = kat.run()
return out['tran']
In the Finesse code you can replace the xaxis
command with noxaxis
so that a call to kat.run()
returns only one value, which is necessary for minimize_scalar()
to work.
Then you can call the scipy function to find the minimum of my_function()
as:
import scipy.optimize as opt
opt.minimize_scalar(my_function, ...)
Answer:
We start by copying basekat
, adding a photo diode detecting transmitted power, and adding simulation instructions. Note that we use the command noxaxis
, which means that we just runs the current setup, i.e., only one data point.
kat2 = basekat.deepcopy() # Copying basekat
# Adding detectors and simulation instructions
code = '''
## Detectors ##
pd tran nc4 # Measuringn transmitted power
## Simulation instructions ##
noxaxis # Changing nothing, just running current setup.
yaxis abs # Returns absolute value of the detector output
'''
# Parsing the FINESSE code
kat2.parseCommands(code)
Now we write the function find_fsr()
that has our kat
-object as the only input, and it returns the FSR of the cavity as long as we have a detector measuring the transmitted power, and no other detectors. As you see below, the function find_fsr()
has two inner functions as well: one that finds maximums, and one that finds minimums. Note that the only difference is the minus sign in the output for the max-finder. find_fsr
operates in three steps:
minimize_scalar()
, and stores itminimize_scalar()
to find the minimum in between the two maximums.minimize_scalar()
.import scipy.optimize as op
def find_fsr(kat):
# Function for finding FSR of a cavity
# Finding the laser component of the kat-object
laser = kat.getAll(pykat.components.laser)[0]
def find_max(f, df):
# Function that is minimised at the frequency f + df
# that gives maximum transmitted power. Since Brent's
# method will search around f = 0, thus df acts as the
# starting guess.
laser.f = f + df # Setting new laser frequency
out_max = kat.run() # Running
return -out_max.y[0,0] # Output
def find_min(f, df):
# Function that is minimised at the frequency f + df
# that gives minimum transmitted power. Since Brent's
# method will search around f = 0, thus df acts as the
# starting guess.
laser.f = f + df # Setting new laser frequency
out_min = kat.run() # Running
return out_min.y[0,0] # Output
# Step 1: Finding the first peak
# -----------------------------
offset = 0 # Start guess, df = 0
xtol = 1.0e-6
opts = {'xtol': xtol, 'maxiter': 500}
# Calling minimize_scalar with Brent's method to
# find minimum of find_max().
sol_1 = op.minimize_scalar(find_max,
args=(offset),
method='brent',
options=opts)
max1 = sol_1.x # Storing position
# Step 2: Estimating minimum between peaks
# ------------------------------
xtol = 1.0e-1 # This step is a rough estimation, using higher tolerance
opts = {'xtol': xtol, 'maxiter': 500}
offset = max1+10*xtol # Start guess, a bit away from max1
# Calling minimize_scalar with Brent's method to
# find minimum of find_min().
sol_2 = op.minimize_scalar(find_min,
args=(offset),
method='brent',
options=opts)
min1 = sol_2.x + offset
# Step 3: Finding second peak
# ------------------------------
xtol = 1.0e-6 # Lowering tolerance again
opts = {'xtol': xtol, 'maxiter': 500}
offset = 2*min1 - max1 # Starting guess based on previous steps
# Calling minimize_scalar with Brent's method to
# find minimum of find_max().
sol_3 = op.minimize_scalar(find_max,
args=(offset),
method='brent',
options=opts)
max2 = sol_3.x + offset # Storing peak position
fsr = np.abs(max2-max1) # Computing the FSR
# Prints the FSR
print('L = {0:.1f} m, FSR = {1:.2f} Hz'
.format(kat.sL.L.value, fsr))
return fsr
Now we test our function for 5 different cavity lengths.
lengths = np.logspace(0,4,5) # Lenghts, log-spaced between 1 and 10 000.
fsr = []
for L in lengths:
kat2.sL.L = L # Setting cavity length
fsr.append(find_fsr(kat2)) # Finding FSR and storing in list fsr
Plotting the results:
# Analytical FSR for comparison
c = 299792458.0 # Speed of light
FSR = c/(2*lengths)
# Plotting
fig2 = plt.figure(figsize=(8,4))
ax = plt.subplot(1,1,1)
ax.plot(1/lengths,FSR,'r-',label='Analytical')
ax.plot(1/lengths,fsr,'bo',label='Simulation')
ax.set_xlabel('1/Cavity length [1/m]')
ax.set_ylabel('FSR [Hz]')
ax.set_xscale('log')
ax.set_yscale('log')
ax.legend(loc = 2)
plt.show(fig2)
As we see in the figure, the FSR is inversely proportional to the cavity length. We can also see that the simulated points perfectly agrees with the analytical expression $\mathrm{FSR}=c/(2L)$, where $c$ is the speed of light, and $L$ is the cavity length.
To derive this analytical expression we used the resonance condition $2kL = 2n\pi$, where n is an arbitrary integer, and $k = 2\pi/\lambda = 2\pi f/c$ is the wavenumber. Thus, we get
which determines all frequencies that gives resonance peaks. To obtain the FSR we need to compute the frequency distance between two adjacent peaks. Since they are linearly spaced, we can for simplicity chose the frequency distance between the peaks for n=0 and n=1, which results in an FSR of
Writing this much code for computing something that has such a simple analytical solution is a bit of an over kill. But it is good Python and optimisation practice. Also, in FINESSE, there are two quicker ways of finding out the FSR of a cavity. You can either use the cavity parameter detector (keyword cp
), or the command trace 2
. You can read about these commands in the online syntax reference [3] and in the FINESSE manual [2]. However, both commands currently demands a stable cavity and that you use a Gaussian beam in comparison to the plane waves we are currently using. So we will wait with explaining those commands until chapter 3.
In this session we have ...