An optical cavity¶
The best way to familiarise yourself with Finesse is with example models. In this section we will cover the simple case of modelling resonances in a Fabry-Perot cavity as a starting point.
The optical configuration¶
In the figure below we show the layout that we want to model. This consists of an input laser incident upon a two-mirror optical cavity. Measuring the reflected, transmitted and circulating power requires the use of three photodiodes - PDrefl, PDtrans and PDcirc, respectively.
To find the resonances of this cavity we can either change the tuning(s) of the mirrors
or scan over the frequency of the input laser. In this case we will alter the tuning of
the input mirror m1
in the range \(\phi_{\mathrm{m1}} \in [-180^{\circ},
180^{\circ}]\) to show multiple (repeated) resonances.
The Finesse model¶
To represent the above layout with a Finesse model we can write the following Python code:
import finesse
finesse.configure(plotting=True)
kat = finesse.Model()
kat.parse(
"""
# Add a Laser named L0 with a power of 1 W.
l L0 P=1
# Space attaching L0 <-> m1 with length of 0 m (default).
s s0 L0.p1 m1.p1
# Input mirror of cavity.
m m1 R=0.99 T=0.01
# Intra-cavity space with length of 1 m.
s CAV m1.p2 m2.p1 L=1
# End mirror of cavity.
m m2 R=0.991 T=0.009
# Power detectors on reflection, circulation and transmission.
pd refl m1.p1.o
pd circ m2.p1.i
pd trns m2.p2.o
"""
)
Firstly, we call configure()
with plotting=True
. This sets up some defaults
for plotting styles, such as line colours and font size. Next, we create a new
Model
, and then parse our KatScript (see KatScript for more details on
the syntax used). This creates a model of a Fabry-Perot cavity with a length of 1 metre
and nerly equal reflectivities on both the input mirror and end mirror. The input laser power
is set to 1 W.
Plotting the outputs¶
To run this model and plot the outputs from the power-detectors we can simply write:
# Scan over the detuning DOF of m1 from -180 deg to +180 deg with 400 points.
out = kat.run("xaxis(m1.phi, lin, -180, 180, 400)")
out.plot(logy=True);
The call to run()
is passed an Action
that will operate on the model.
In this case it is an Xaxis
, which sweeps some parameter of the model
between two limits. This returns an ArraySolution
, containing the
output of each detector at each point on the axis. We then plot the detector outputs,
via plot()
. The logy
flag tells the plotting
routines to use a log-scale on the y-axis of any produced figures.
For more details on actions and analyses, see the analyses introduction.
Plotting the beam profile¶
To plot the beam profile, we need to add some curvatures to our mirrors and define the cavity. This will implicitly switch the simulation performed by Finesse from plane wave to Hermite-Gaussian.
# Add some curvatures to our mirrors.
kat.m1.Rc = -0.7
kat.m2.Rc = 0.7
# Define the cavity.
kat.parse("cavity cavity1 source=m1.p2.o via=m2.p1.i priority=1")
# Plot the beam trace, starting from cavity the eigenmode.
tsy = finesse.tracing.tools.propagate_beam(
to_node=kat.L0.p1.i, from_node=kat.m2.p1.o, direction="y"
)
tsy.plot();
The resulting plot shows the cavity eigenmode. To make the plot more interesting, we could add a lens to the input coupler. To do this, we build a compound mirror:
# Remove s0 so we can add in a lens.
# FIXME kat.remove is not yet working, so we use a list
# comprehension to remove s0 from the model and
# reparse it into a new model. (See unparse below)
kat2 = finesse.Model()
kat2.parse(
"\n".join(
[l for l in kat.unparse().splitlines() if "s0" not in l]
)
)
# Add a silicon lens with an AR surface to the input coupler.
kat2.parse(
"""
space s0 L0.p1 m1_AR.p1 L=0.5
mirror m1_AR R=100e-6 L=10e-6 Rc=-0.3
space s_M1_sub portA=m1_AR.p2 portB=m1.p1 L=10e-3 nr=3.5
"""
)
# Plot beam trace, starting from the cavity eigenmode.
tsy = finesse.tracing.tools.propagate_beam(
to_node=kat2.L0.p1.i, from_node=kat2.m2.p1.o, direction="y"
)
tsy.plot();
Printing model information¶
There are three options for outputting a textual summary of the model. Firstly, printing the model directly gives the usual Python object information:
print(kat)
<finesse.model.Model object at 0x7e53293ded50>
Secondly, you can output a tree view of the nodal network with components as nodes and connected components as branches. Such a tree can be printed in the following way:
print(kat.component_tree(kat.L0))
○ L0
╰──○ m1
╰──○ m2
The argument to component_tree()
is the component to start from (the
tree’s root). Any component in the model can be the tree root.
Note
Models that contain cyclic connections (such as Sagnac interferometers) will not show one of the connections that would create the cycle, to prevent infinite recursion.
Lastly, the most verbose option is to unparse()
the model. This returns
KatScript which can be used to recreate the model.
print(kat.unparse())
# Add a Laser named L0 with a power of 1 W.
l L0 P=1
# Space attaching L0 <-> m1 with length of 0 m (default).
s s0 L0.p1 m1.p1
# Input mirror of cavity.
m m1 R=0.99 T=0.01 L=0.0 Rc=[-0.7, -0.7]
# Intra-cavity space with length of 1 m.
s CAV m1.p2 m2.p1 L=1
# End mirror of cavity.
m m2 R=0.991 T=0.009 L=0.0 Rc=[0.7, 0.7]
# Power detectors on reflection, circulation and transmission.
pd refl m1.p1.o
pd circ m2.p1.i
pd trns m2.p2.o
cavity cavity1 source=m1.p2.o via=m2.p1.i priority=1
# Items below could not be matched to original script, or were not present when the model was originally parsed.
modes(maxtem=0)