Example of migration from Pykat + Finesse 2

It may be easiest to get started by seeing an example of the same simulation done with both Finesse 2 and Finesse 3. The simulation below plots the quantum noise limited sensivity of a plane-wave Michelson with arm cavities.

Finesse 2

Simulations using Pykat and Finesse 2 often follow a similar pattern:

  1. Import Pykat and set up the default plot style.

  2. Construct a model to use as a base

  3. Make a deepcopy of the model to modify temporarily

  4. Add some script to the deepcopy, and perform an xaxis or similar.

  5. Plot the result

  6. Repeat steps 3-6 as needed

import pykat
pykat.init_pykat_plotting()

base = pykat.finesse.kat()
base.parse("""
###########################################################################
###   Variables
###########################################################################
const fmod 40M
const mirror_mass 40
const Larm 4000
const Rcarm 2500
const RcIMC 3

###########################################################################
###   Laser
###########################################################################
l laser 1 0 0 nLaserOut

###########################################################################
###   Input mode cleaner
###########################################################################
s lIMCin 0 nLaserOut nIMC1in

const imc_alpha 3.97
const imc_length_side 0.36
const imc_length_diag 0.3635

bs1 IMC1 0.006 6e-6 0 $imc_alpha nIMC1in nIMC1refl nIMC1trans nIMC1fromIMC4
s lIMC12 $imc_length_side nIMC1trans nIMC2in
bs1 IMC2 0.006 6e-6 0 $imc_alpha nIMC2in nIMC2refl nIMCout dump
s lIMC23 $imc_length_diag nIMC2refl nIMC3in
bs1 IMC3 7.5e-5 6e-6 0 $imc_alpha nIMC3in nIMC3refl dump dump
s lIMC34 $imc_length_side nIMC3refl nIMC4in
bs1 IMC4 7.5e-5 6e-6 0 $imc_alpha nIMC4in nIMC4refl dump dump
s lIMC41 $imc_length_side nIMC4refl nIMC1fromIMC4

attr IMC3 Rc $RcIMC
attr IMC4 Rc $RcIMC

cav cav_IMC IMC1 nIMC1trans IMC1 nIMC1fromIMC4

###########################################################################
###   Modulator
###########################################################################
s lModulatorIn 0 nIMCout nMod1
mod modulator $fmod 0.15 1 pm 0 nMod1 nModulatorOut

###########################################################################
###   Central beamsplitter
###########################################################################
s lmod_bs 1 nModulatorOut nBSin

bs1 BS 0.5 0 2 0 nBSin nBSy nBSx nout

###########################################################################
###   Y arm
###########################################################################
s ly1 5.5 nBSy nITMy1

m1 ITMy 0.005 5e-05 90 nITMy1 nITMy2
attr ITMy mass $mirror_mass Rc -$Rcarm
s LY $Larm nITMy2 nETMy1
m1 ETMy 5e-06 5e-05 90 nETMy1 nETMy2
attr ETMy mass $mirror_mass Rc $Rcarm

cav cACy ITMy nITMy2 ETMy nETMy1

###########################################################################
###   X arm
###########################################################################
s lx1 5.5005 nBSx nITMx1

m1 ITMx 0.005 5e-05 0 nITMx1 nITMx2
attr ITMx mass $mirror_mass Rc -$Rcarm
s LX $Larm nITMx2 nETMx1
m1 ETMx 5e-06 5e-05 0 nETMx1 nETMx2
attr ETMx mass $mirror_mass Rc $Rcarm

cav cACy ITMx nITMx2 ETMx nETMx1

###########################################################################
###   Output
###########################################################################
qnoisedS NSR 1 $fs 0 nout
pd pdo nout
""")

def get_QNLS(kat, xaxis=[0.1, 100, 100]):
    _kat = kat.deepcopy()
    _kat.parse("""
        maxtem off

        fsig DARMx LX 1 0
        fsig DARMy LY 1 180

        xaxis DARMx f log {} {} {}
        yaxis log abs
    """.format(*xaxis))

    return _kat.run()

out = get_QNLS(base)
out.plot(["NSR"])
getting_started/images/migration_finesse2.*

Finesse 3

In Finesse 3, it’s possible to follow a very similar pattern. We start in the same way, importing Finesse and setting up the plot style:

import finesse
finesse.plotting.init()

Next, we construct the model to be used. The syntax of KatScript has changed; for details, see KatScript and Finesse 2 support. A few of the most visible differences here are:

  • Nodes are no longer named or defined by the user when creating a component. Instead, spaces connect directly to a component’s ports, which house the appropriate nodes. For more details, see The port and node system

  • Components can now accept both positional and keyword arguments to set parameters. Most parameters have default values, and can be omitted.

  • Mass is no longer an attribute of optics. Instead, Finesse 3 can model mechanical components, such as the free_mass used below. These connect to an optic’s mech port.

  • Cavities often only need a single node as an argument, and will find the shortest route back to the same component. See Defining the modal basis for a more detailed description.

  • The qnoised detector now implicitly performs a demodulation at the signal frequency, so this isn’t specified directly.

base = finesse.Model()
base.parse("""
###########################################################################
###   Variables
###########################################################################
var fmod 40M
var mirror_mass 40
var Larm 4000
var Rcarm 2500
var RcIMC 3

###########################################################################
###   Laser
###########################################################################
l laser P=1

###########################################################################
###   Input mode cleaner
###########################################################################
s lIMCin laser.p1 IMC1.p1

var imc_alpha 3.97
var imc_length_side 0.36
var imc_length_diag 0.3635

bs IMC1 T=0.006 L=6e-6 alpha=&imc_alpha
s lIMC12 IMC1.p3 IMC2.p1 L=&imc_length_side
bs IMC2 T=0.006 L=6e-6 alpha=&imc_alpha
s lIMC23 IMC2.p2 IMC3.p1 L=&imc_length_diag
bs IMC3 T=7.5e-5 L=6e-6 alpha=&imc_alpha Rc=&RcIMC
s lIMC34 IMC3.p2 IMC4.p1 L=&imc_length_side
bs IMC4 T=7.5e-5 L=6e-6 alpha=&imc_alpha Rc=&RcIMC
s lIMC41 IMC4.p2 IMC1.p4 L=&imc_length_diag

cav cav_IMC IMC1.p3.o

###########################################################################
###   Modulator
###########################################################################
s lModulatorIn IMC2.p3 modulator.p1
mod modulator f=&fmod midx=0.15 mod_type=pm order=1

###########################################################################
###   Central beamsplitter
###########################################################################
s lmod_bs modulator.p2 BS.p1 L=1
bs BS T=0.5 L=0 phi=2

###########################################################################
###   Y arm
###########################################################################
s ly1 BS.p2 ITMy.p1 L=5.5

m ITMy T=0.005 L=5e-5 phi=90 Rc=-&Rcarm
s LY ITMy.p2 ETMy.p1 L=&Larm
m ETMy T=5e-6 L=5e-5 phi=90 Rc=&Rcarm

free_mass sus_ITMy ITMy.mech mass=&mirror_mass
free_mass sus_ETMy ETMy.mech mass=&mirror_mass

cav cACy ITMy.p2.o

###########################################################################
###   X arm
###########################################################################
s lx1 BS.p3 ITMx.p1 L=5.5005

m ITMx T=0.005 L=5e-5 Rc=-&Rcarm
s LX ITMx.p2 ETMx.p1 L=&Larm
m ETMx T=5e-6 L=5e-5 Rc=&Rcarm

free_mass sus_ITMx ITMx.mech mass=&mirror_mass
free_mass sus_ETMx ETMx.mech mass=&mirror_mass

cav cACx ITMx.p2.o

###########################################################################
###   Output
###########################################################################
qnoised NSR BS.p4.o nsr=true
pd pdo BS.p4.o
""")

Performing the simulation is also very similar, with some more noticeable changes:

  • The Finesse 2 fsig command has been split into two parts. fsig is now a command that only sets the signal frequency to use, while sgen injects this signal into components.

  • xaxis can still be defined as part of the KatScript, but it’s often more useful to use the Python API actions such as Xaxis instead. These actions are one of the most powerful features of Finesse 3, so it’s definitely worth checking out Actions, Analyses, and Solutions for more details.

  • yaxis has gone, and instead can be replaced by arguments to plot()

from finesse.analysis import xaxis

def get_QNLS(kat, _xaxis=[0.1, 100, 100]):
    _kat = kat.deepcopy()
    _kat.parse(f"""
        sgen DARMx LX.h
        sgen DARMy LY.h phase=180
        fsig(1)
    """)

    _kat.switch_off_homs()

    return xaxis(_kat.fsig.f, "log", *_xaxis)

out = get_QNLS(base)
out.plot(["NSR"], log=True);
../_images/migration_example_2_0.svg