## Using parakat to speed up 2D Finesse runs

A more complex/realistic example demonstrating how parakat can be used to speed up 2-dimentional simulations is provided at the end of this post.

The example Finesse code used in Parallelising Finesse 2 + PyKat: three ways is deliberately simple and quick so we can demonstrate some basic functionality. However it’s not a situation you’d realistically encounter, or one that would likely motivate you to try parallelising your code in the first place.

In this example, the user wanted to model a 2-mirror cavity that has a slightly misaligned input mirror (therefore requiring the use of higher order modes). They are interested in understanding how the cavity resonant conditions change with this misalignment, so want to compare cavity scans (circulating power as a function of cavity tuning) as the value of the misalignment is varied.

The base Finesse code, parsed directly to Pykat, is:

base = finesse.kat()
base.verbose=False
base.parse("""
l L0 1 0 n0
s s0 0 n0 n1
m m1 0.95 0.05 0 n1 n2
s s1 100 n2 n3
m m2 0.95 0.05 0 n3 n4
attr m2 Rc 150
attr m2 xbeta 1u
cav c1 m1 n2 m2 n3
pd pow n2
maxtem 3
""")

NB: as-written, this example uses maxtem 3 and few (80) steps along each axis, so that it runs quite fast for demonstration purposes. it will quickly get a lot slower when these numbers are increased, as you would likely need to do for a more complete and accurate understanding of the result.

## Standard, non-parallel method

Finesse can handle multiple x-axes commands. It does this in series by scanning the parameter of the xaxis for each x2axis value in sequence. The syntax is simply:

kat = base.deepcopy()
kat.parse("""
xaxis m2 phi lin -90 90 80
x2axis m1 xbeta lin 0 80u 80
""")
out = kat.run()

which will directly (eventually) produce a 2D output object out["pow"], along with a list of the input parameter choices in out.x and out.y [check!] for the xaxis and x2axis parameters respectively, that can be plotted however you like.

## Using parakat to speed things up

We can speed this up by instead just calculating the xaxis and splitting the x2axis into chunks that run in parallel on different engines.

Using ipcluster via subprocess we first start 8 engines:

n_eng = 8 # max number of parallel tasks

subprocess.run(f"ipcluster start -n {n_eng} --daemonize",shell=True)
time.sleep(10) # wait a few sec for the cluster to start up

Then set up the equivalent of the x2axis command above, using a for-loop to create each kat object we’ll need and distribute these between the 8 engines:

xstart = 0
xstop = 80e-6
xpoints = 201
xaxis = np.linspace(xstart,xstop, xpoints) #equivalent to x2axis above
sp = np.array_split(xaxis,n_eng) #separate this into 8 smaller sections that run simultaneously

pk = parakat()
for T in np.arange(n_eng):
kat = base.deepcopy()
kat.parse("xaxis m2 phi lin -90 90 200") #same as before
kat.parse(f"x2axis m1 xbeta lin {sp[T][0]}  {sp[T][-1]} {len(sp[T])-1}") # sub-section of old x2axis
pk.run(kat)
outs = pk.getResults()

# Stopping the cluster
subprocess.run(f"ipcluster stop",shell=True)
time.sleep(3) #wait a sec for the cluster to shut down

This time, we have to manually merge our 8 pieces of output data back into one plot:

def twod(b):
# reducing output data from 3D array (1,n,m) to 2D array (n,m)
#print(np.shape(b))
return np.reshape(b, (b.shape[1], b.shape[2]))

x = outs[0].x
y = xaxis
data = twod(outs[0].z)
for T in np.arange(1,n_eng):
#print(T)
data = np.vstack([data,twod(outs[T].z)])

Now, as before, we can use the final 2D object called data, along with full input parameter lists x and y, to plot the data as we want.

fig, ax = plt.subplots(1,1, figsize=(12,6))
ax.imshow(data, norm=LogNorm(), aspect='equal')