Skip to content

Instantly share code, notes, and snippets.

@g2hollow
Last active December 17, 2024 08:48
Show Gist options
  • Save g2hollow/9c6954749e9da7786cbee373909dca3c to your computer and use it in GitHub Desktop.
Save g2hollow/9c6954749e9da7786cbee373909dca3c to your computer and use it in GitHub Desktop.
klayout macro pcell sprial
import pya
import math
"""
This sample PCell implements a library called "MyLib" with a single PCell that
draws a sprial. It demonstrates the basic implementation techniques for a PCell
and how to use the "guiding shape" feature to implement a handle for the inner
and out radii of the spiral.
NOTE: after changing the code, the macro needs to be rerun to install the new
implementation. The macro is also set to "auto run" to install the PCell
when KLayout is run.
"""
class Spiral(pya.PCellDeclarationHelper):
"""
The PCell declaration for the sprial
"""
def __init__(self):
# Important: initialize the super class
super(Spiral, self).__init__()
# declare the parameters
self.param("l", self.TypeLayer, "Layer", default = pya.LayerInfo(1, 0))
self.param("n", self.TypeInt, "Number of points", default = 64)
self.param("inner_r", self.TypeDouble, "Inner Radius", default = 1)
self.param("outer_r", self.TypeDouble, "Outer Radius", default = 10)
self.param("width", self.TypeDouble, "Width", default = 1)
self.param("spacing", self.TypeDouble, "Spacing", default = 1)
self.param("inner_handle", self.TypeShape, "", default = pya.DPoint(0, 0))
self.param("outer_handle", self.TypeShape, "", default = pya.DPoint(0, 0))
# this hidden parameter is used to determine whether the radii have changed
# or the "inner/outer" handles have been moved
self.param("inner_mem", self.TypeDouble, "Inner_mem", default = 0.0, hidden = True)
self.param("outer_mem", self.TypeDouble, "outer_mem", default = 0.0, hidden = True)
def display_text_impl(self):
# Provide a descriptive text for the cell
return "Spiral(L=" + str(self.l) + ",Outer radius=" + ('%.3f' % self.outer_r) + ")"
def coerce_parameters_impl(self):
# We employ coerce_parameters_impl to decide whether the handle or the
# numeric parameter has changed (by comparing against the effective
# radii *_handle_radius) and set *_mem to the effective radius. We also update the
# numerical value or the shape, depending on which on has not changed.
inner_handle_radius = None
outer_handle_radius = None
if isinstance(self.inner_handle, pya.DPoint):
# compute distance in micron
inner_handle_radius = self.inner_handle.distance(pya.DPoint(0, 0))
if isinstance(self.outer_handle, pya.DPoint):
# compute distance in micron
outer_handle_radius = self.outer_handle.distance(pya.DPoint(0, 0))
if abs(self.inner_r-self.inner_mem) < 1e-6 and abs(self.outer_r-self.outer_mem) < 1e-6:
self.inner_mem = inner_handle_radius
self.inner_r = inner_handle_radius
self.outer_mem = outer_handle_radius
self.outer_r = outer_handle_radius
else:
self.inner_mem = self.inner_r
self.inner_handle = pya.DPoint(-self.inner_r, 0)
self.outer_mem = self.outer_r
self.outer_handle = pya.DPoint(-self.outer_r, 0)
# n must be larger or equal than 4
if self.n <= 4:
self.n = 4
def can_create_from_shape_impl(self):
# Implement the "Create PCell from shape" protocol: we can use any shape which
# has a finite bounding box
return self.shape.is_box() or self.shape.is_polygon() or self.shape.is_path()
def parameters_from_shape_impl(self):
# Implement the "Create PCell from shape" protocol: we set inner/outr_r and l
#from the shape's bounding box width and layer
self.inner_r = self.shape.bbox().width() * self.layout.dbu / 4
self.outer_r = self.shape.bbox().width() * self.layout.dbu / 2
self.l = self.layout.get_info(self.layer)
def transformation_from_shape_impl(self):
# Implement the "Create PCell from shape" protocol: we use the center of the shape's
# bounding box to determine the transformation
return pya.Trans(self.shape.bbox().center())
def produce_impl(self):
# This is the main part of the implementation: create the layout
# fetch the parameters convert to database units
inner_r_dbu = self.inner_r / self.layout.dbu
outer_r_dbu = self.outer_r / self.layout.dbu
# compute the spiral
pts = []
da = math.pi * 2 / self.n
dr = (self.width+self.spacing)/self.n/self.layout.dbu
current_radius = inner_r_dbu
current_angle = 0
while current_radius < outer_r_dbu:
pts.append(pya.Point.from_dpoint(pya.DPoint(current_radius * math.cos(current_angle), current_radius * math.sin(current_angle))))
current_radius += dr
current_angle = (current_angle+da)%(math.pi*2)
# create the shape
self.cell.shapes(self.l_layer).insert(pya.Path(pts,self.width/self.layout.dbu))
class MyLib(pya.Library):
"""
The library where we will put the PCell into
"""
def __init__(self):
# Set the description
self.description = "My First Library"
# Create the PCell declarations
self.layout().register_pcell("Spiral", Spiral())
# That would be the place to put in more PCells ...
# Register us with the name "MyLib".
# If a library with that name already existed, it will be replaced then.
self.register("MyLib")
# Instantiate and register the library
MyLib()
@shimachem
Copy link

Hello and thank you for your nice video. I am trying to make a Macro for serpentine pattern. Could you please help me?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment