Creating array of circles using KLayout and Python

Aditya Prabaswara
4 min readMay 5, 2021

--

Lately I have been using FIB to do some selective area growth process for GaN nanorods by patterning substrates covered with TiN as a mask. One part of the process is of course drawing the mask that I will use to make opening where the GaN nanorods will grow. In the case of FIB, the ion beam will selectively remove the TiN, exposing the substrate where GaN will nucleate and form nanorods.

I typically use KLayout to draw my patterns and later upload it into the FIB computer. Now, the pattern of a nanorod array is quite simple as it is basically just an array of circles and can be done quickly through the GUI. But just to make things more interesting and quickly reconfigurable, I decided to incorporate a bit of Python scripting to learn how to do mask design via Python — because apparently mask design jobs in the photonics industry require me to know how to do this — and just because I kind of enjoy learning new things.

Unfortunately the KLayout Python scripting tutorial and documentation is kind of fragmented, so I had to piece together informations from other forum posts and tutorials, and wrestle with the script for a couple of hours just to get this basic pattern done. Hopefully, I will be able to incorporate what I’ve learned here for more complex designs in the future.

So to start with, import the libraries. Refer to the official documentation on how to install the required library.

import pya
import numpy as np

Now, create the layout for the design. In this snippet, I created the layout, while adding a top cell called ‘top’ where we will make our array, another cell for the basic unit cell of the aray called ‘circle’ stored inside the variable ‘c_circle’, and a single layer ‘1,0’, stored inside the variable ‘l_circle’. Note that the expression on the right hand side is the name that will show inside KLayout, and the left hand side is the variable that will be used by the Python script.

#initialize
layout = pya.Layout()
top = layout.create_cell('top')
c_circle = layout.create_cell('circle')
l_circle = layout.layer(1,0)

Since the pya package doesn’t have a circle readily available, we can define a Pcell called ‘circle’ using the following lines of code.

# make a circle pcell
radius = 200 #radius in nm
nr_points = 32 #number of points
#create an array of angles
angles = np.linspace(0,2*np.pi,nr_points+1)[0:-1]
points = [] #array of point
for angle in angles:
points.append(pya.Point(radius*np.cos(angle),radius*np.sin(angle)))
circle = pya.SimplePolygon(point)

In a nutshell, we first define the circle radius and the number of point we will use to draw the outline of our circle. Hint: if you put nr_points as 6, you will get hexagon instead. Next, we simply create an array of angles from 0 to 2*pi, and calculate every x and y coordinate associated with the circle radius. Finally, we use all the points we have defined and create a circle object.

A basic circle shape

One of the function that’s going to appear frequently is the insert function, where we insert a shape into our cell. As we are going to make a hexagonal grid, a little modification needs to be done before making our array.

#for hexagonal grid
a = 400 #hexagon radius towards flat
#number of instances
i=10
c_circle.shapes(l_circle).insert(circle)
c_circle.shapes(l_circle).insert(circle.moved(a*np.sqrt(3),a))

We defined ‘a’ as the distance between the center of a hexagon to its flat side. ‘i’ is defined as the number of instance within our array, which will be apparent later. In this case I use the same number of instance in both the x and y direction.

In our cell, we put two circle shapes with x and y offset serving as the basic cell of a hexagonal array, which is why we use the insert function twice, with the second line containing x and y offset. The offset calculation for hexagonal array is shown in the schematic below.

Schematic for hexagonal array

Now that we have our basic cell, we can just replicate it using CellInstArray. This function creates an array based on another cell used as a reference. It takes arguments in the following order: reference cell index, transformation, x offset, y offset, number of x instances, and number of y instances. Note that the transformation function pya.Trans can also do other things such as rotation, and mirroring but I haven’t played around with it yet.

Finally, write the output into a file. Typically we use .gds extension, but the FIB software I’m using only accepts .dxf, so that’s what we’re doing.

trans = pya.Trans(pya.Point(0,0))new_instance = pya.CellInstArray(c_circle.cell_index(),trans,pya.Vector(2*a*np.sqrt(3), 0 ), pya.Vector(0, 2*a), i/2, i/2)
top.insert(new_instance)
layout.write("test.dxf")
Final hexagonal array of circles

There you have it. In my case, I will be using this script to create arrays with various circle diameter and pitch.

--

--

Aditya Prabaswara
Aditya Prabaswara

Written by Aditya Prabaswara

PhD student. Interested in technology, popular science, and weeb stuffs.