Finesse includes a built-in definition language called KatScript that provides an alternative way to build models without using the Python API. It is concise, simple, and easy to use. The following contains a brief introduction to KatScript syntax and the available instructions.

Supported elements, commands and analyses are described in their corresponding sections. There are also command, element and analysis indices for quick reference.


KatScript in Finesse 3 is different to that of Finesse 2, however the most common syntax is also supported via a separate legacy parser. There is also a migration guide.

A quick example

KatScript models are built by specifying components, detectors, commands and other types of statement on separate lines:

# Example of 10 W of laser power incident upon a Fabry-Perot cavity.
l l1 P=10
s s1 l1.p1 m1.p1
m m1 R=0.99 T=0.01
s s2 m1.p2 m2.p1
m m2 R=0.99 T=0.01
pd pd1 m2.p1.i
modes(even, maxtem=10)

Each line above specifies a component, detector or command. Most of the statements are formed from a series of space-delimited values; these represent elements of the model. Elements always have unique names, which must be specified after the element type, which is often, but not always, represented by a short one or two character name. The last line is a command which sets some properties of the model or simulation. In this case, it’s setting the higher order modes to simulate. The arguments following the element names are used to customise the element, such as to set the reflectivity and transmissivity in the case of the mirrors. The comma-delimited parameters inside the parentheses of the modes command serve a similar purpose.

Language fundamentals

Whitespace and comments

Whitespace can be simple spaces, tabs and newlines. Comments start with #. Everything including and after # on the same line is ignored by the parser.

# This is a comment!

Whitespace is sometimes important in KatScript, for instance between parameters of elements.

Types, names and keywords

Component types (like mirror), names (like m1) and keywords (like lin) are written directly, without quotation marks:

mirror m1  # A mirror named 'm1'.

User-defined names have to start with a letter, but can otherwise contain integer numbers and underscores.


Numbers can use integer, floating point, scientific and complex notation. The imaginary unit is j. SI prefices are also supported, but cannot be combined with scientific notation. For example:

# Typical number forms.

# Scientific notation.

# SI prefices. Any of p, n, u, m, k, M, G, and T are supported.
1m  # equivalent to 1e-3
1k  # equivalent to 1e3

Components and detectors

Component and detector definitions, collectively referred to as elements, mirror their Python API signatures for the most part. Both positional and keyword arguments are supported. Elements can be referred to using either their full name or an abbreviated short form. They are defined using syntax of the form element name arg1 arg2 arg3=val3 ...:

# Definition with positional arguments (Finesse 2 style): a mirror named "m1" with
# reflectivity R=0.99 and transmissivity T=0.01.
mirror m1 0.99 0.01

# Definition with keyword arguments.
# Note that only two of "R", "T" and "L" are required, and the third is calculated
# from the first two. You can still specify all three, but they must all add up to
# 1.
mirror m2 R=0.99 T=0.01

# You can mix positional and keyword arguments as long as they make sense. Like in
# Python, positional arguments must always come before keyword arguments.
mirror m3 1 0 Rc=[1, 1.1]  # Rcx and Rcy defined together.

Element definitions cannot span multiple lines.

Most components and detectors will have alias’ that can be used instead of longer names. For example, m can be used instead of mirror, or bs instead of beamsplitter.

Commands and analyses

Commands set some special behaviour in the model. Analyses define one or more procedures to perform on the model defined in the script. Both commands and analyses are defined using syntax in the form command(arg1, arg2, ...):

# Command used to set the higher order modes to simulate.

# The `modes` command supports various different types of input. You can for example
# specify individual modes...
modes([[1, 0], [0, 1], [2, 0]])

# ...or odd modes up to 9.
modes(odd, maxtem=9)

The arguments inside the parentheses in command and analysis definitions can span across multiple lines, and may contain comments:

    # Set maximum mode.

Some analyses can contain other analyses; this is used for example to build a set of nested procedures to perform on the model:

# Xaxis function to perform a linear sweep over the value of m1.phi.
xaxis(m1.phi, lin, 0, 10k, 500)

# A nested action tree.
    # First sweep m1.R...
    xaxis(m1.R, lin, 0, 1, 10),
    # ...then L0.P.
    xaxis(L0.P, lin, 10, 100, 20)

Ports and nodes

Some components define ports which act as reference points in the model to which detectors and other components may be connected or certain properties such as beam sizes may be defined. Ports themselves contain nodes which provide access to the underlying directions. For a full explanation, see The port and node system. Some brief examples are shown below:

# Spaces connect directly to component's ports.
l l1 P=1
s s1 l1.p1 m1.p1 L=1
m m1 0.99 0.01
s s1 m1.p2 m2.p1 L=1
m m2 0.99 0.01

# Detectors connect to the nodes within ports, allowing the direction to detect to
# be specified.
pd circ m2.p1.i  # Incoming beam at port p1
pd trans m2.p2.o  # Outgoing beam at port p2


Elements and their attributes and parameters can be addressed elsewhere in models using their path. Elements are simply referred to by their name; for example, a mirror named m1 has path m1. Attribute and parameter paths are formed by combining their owner’s path with the attribute or parameter name, separated by a .; for example, a mirror’s phi model parameter has path m1.phi. Ports and nodes are also addressable this way: the mirror’s first port has path m1.p1 and the incoming node of that port has path m1.p1.i. Commands and analyses do not have names and can thus not be addressed this way.

Paths are mainly useful when specifying spaces, detectors and analyses. Spaces can be used to connect components by referring to the paths of their ports:

m m1 R=0.99 T=0.01
m m2 R=0.99 T=0.01
s s1 m1.p2 m2.p1 L=10  # Connect port 2 of m1 to port 1 of m2.

Detectors can “look at” a particular port or node in a model:

l l1 P=10
m m1 R=0.99 T=0.01
s s1 l1.p1 m1.p1
pd pd1 m1.p1.i  # Look at the power incident on m1 on the port 1 side
pd pd2 m1.p2.o  # Look at the power outgoing from m1 on the port 2 side
ad a m1.p2.o f=0


The syntax of Finesse 3 detector code has changed the order of arguments compared to version 2. The nodes are now specified after the name of the detector, rather than at the end of the command. This is because we can now allow keyword-arguments and other optional parameters which are easier to parse when all the required arguments are first.

Certain analyses can sweep model parameters:

l l1 P=1
m m1 R=0.99 T=0.01
m m2 R=0.99 T=0.01
s s1 m1.p2 m2.p1 L=10
xaxis(l1.P, lin, 0, 10, 100)  # Sweep l1's power from 0 to 10 W over 100 points.

Expressions and references

Parameter values can contain expressions:

m m2 R=0.5 T=0.5 phi=cos(2*pi)

When used in element definitions, expressions cannot contain spaces. Spaces are allowed when the expression is enclosed in parentheses, either inside a function or parentheses:

# Allowed:
l l2 P=(&l1.P / 10)

# Functions also work:
l l2 P=cos(&l1.P / 10)

# NOT allowed:
# l l2 P=&l1.P / 10

Supported operators

The following operators are supported:












Floor division



Supported expression functions

The following mathematical functions are supported:




Absolute value








Real part


Imaginary part














(Two-argument) arctangent


Square root


Degrees to radians


Radians to degrees

Functions that require multiple arguments (currently only arctan2) should have those arguments separated by a comma:

arctan2(1, 0)


Expressions in KatScript can make references to model parameters by prefixing the parameter’s address with an ampersand (&). This allows the value of the target model parameter to be used inside expressions. Since expressions can themselves be the value of other model parameters, this lets model parameters be linked:

m m1 0.99 0.01
m m2 R=&m1.R T=&m1.T  # m2's R and T parameters take their values from those of m1.

References can be included within larger expressions:

l l1 P=1
l l2 P=2*&l1.P  # Set l2's power to twice that of l1.

References can also refer to other parameters within the same element:

m m1 R=1-&m1.T T=0.01  # Set R to ensure R + T = 1.

Model parameter values containing references (either directly or via expressions) get updated on each step of simulations, allowing for a single axis sweep to update many parameters indirectly. This is a powerful feature of KatScript, allowing for compact descriptions of complex models.


The following keywords exist in KatScript and serve special purposes. These do not need to be specified in quotes when used as element or function arguments:


Used to represent null.

even, odd, x, y, off

Used to represent higher order mode sets.

lin, log

Used to represent scales.

am, pm

Used to represent modulation types.

lowpass, highpass, bandpass, bandstop

Used to represent filter types.

div, gouy, l, length, loss, finesse, fsr, fwhm, pole, modesep, q, g, rc, stability, s, resolution, w, w0, z, r, tau

Cavity and beam properties.


Some parameters, like the Rc parameter of a mirror or the modes command support arrays as inputs. Arrays in KatScript are defined using comma-delimited lists of values contained within square brackets:

m m1 R=1 T=0 Rc=[1, 1.1]  # Radius of curvature in tangential (x) and sagittal (y) planes.

Arrays can also contain expressions:

m m1 R=1 T=0 Rc=[1, 1.1]
m m2 R=1 T=0 Rc=[&m1.Rcx, 1.2]

Arrays can also contain other arrays. This can be useful for defining the modes to simulate:

modes([[1, 0], [0, 1], [2, 0]])  # Simulate only these higher order modes.

Generating KatScript from a model

Once you’ve parsed a script into a model, you can continue to make changes using the Python API. If you wish to then regenerate KatScript for the updated model, you can use Model.unparse() or Model.unparse_file(). Note that this does not (yet) preserve the original parse order.

from finesse import Model

model = Model()

# Parse a simple model.
    l l1 P=10
    s s1 l1.p1 m1.p1
    m m1 R=0.995 T=1-&m1.R
    s s2 m1.p2 m2.p1 L=10
    m m2 R=0.9999 T=1-&m2.R
    pd pd1 m2.p1.i
    pd pd2 m2.p2.o

# Change the cavity length.
model.spaces.s2.L = 9

# Unparse the model.
laser l1 P=10.0 f=0.0 phase=0.0
mirror m1 R=0.995 T=(1-&m1.R) L=none phi=0.0 Rc=[inf, inf] xbeta=0.0 ybeta=0.0 misaligned=false
space s1 portA=l1.p1 portB=m1.p1 L=0.0 nr=1.0 gouy_x=0.0 gouy_y=0.0
mirror m2 R=0.9999 T=(1-&m2.R) L=none phi=0.0 Rc=[inf, inf] xbeta=0.0 ybeta=0.0 misaligned=false
space s2 portA=m1.p2 portB=m2.p1 L=9.0 nr=1.0 gouy_x=0.0 gouy_y=0.0
power_detector_dc pd1 node=m2.p1.i pdtype=none
power_detector_dc pd2 node=m2.p2.o pdtype=none


KatScript generation is an experimental feature and likely contains bugs. It is not (yet!) guaranteed to generate KatScript that parses back to the same model state. Use it with care!