Setting Object Attributes

I have been trying to do something very simple, which I do not know why it is not working. I am trying to change some of the attributes of the object pya.Path, but when I do none of the changes actually show up in the PCell on KLayout. Printing out the object attributes should that it is setting it to the value I want, but it is no effect of the outputted PCell. Attached below is a snippet of code that should hopefully clarify my problem:

def produce_impl(self):

# fetch the parameters
w_dbu = self.w / self.layout.dbu
length_dbu = self.length / self.layout.dbu

pts_track = []


pt1 = [(-length_dbu/2,-w_dbu/2),(-length_dbu/2,w_dbu/2),\
  (length_dbu/2, w_dbu/2),(length_dbu/2,-w_dbu/2)] #random points, not particularly important


for i in pt1:
  pts_track.append(pya.Point.from_dpoint(pya.DPoint(i[0],i[1])))

special_path = pya.Path

special_path.end_ext =  2.5/self.layout.dbu #does not change anything in KLayout 


# create the shape
self.cell.shapes(self.l_layer).insert(special_path(pts_track, w_dbu))

Comments

  • I should also mention that this is not just specific to the attribute "end_txt" as I have tried others. I must be doing something fundamentally wrong so any help would be much appreciated!

  • You're fundamentally wrong in your understanding of Python.

    Here is what you do:

    special_path = pya.Path
    

    This does not create a path object, but gives you the Path type object.

    special_path.end_ext = ...
    

    This sets a new attribute on the type object with no further effect. You just contaminated it.

    special_path(pts_track, w_dbu)
    

    Now you call the type object which will actually create the path. Calling a type object creates a new object of the type, passing the arguments to the new object's __init__ constructor.

    So setting "end_ext" of course does not have an effect.

    The correct code is that:

    special_path = pya.Path(pts_track, w_dbu)
    special_path.end_ext = 2.5 / self.layout.dbu
    self.cell.shapes(self.l_layer).insert(special_path)
    

    Matthias

  • edited October 2023

    Thanks for the quick response Matthias! I realized quickly that this was the case and it seemed to have resolve the issue, but now that I have a more complex geometry (meandering path), this issue occurs even when calling the object in the correct order. Below is what I am referring too:

    import pya
    import numpy as np
    from itertools import cycle
    
    class Resonator_LC_Lumped(pya.PCellDeclarationHelper):
    
      def __init__(self):
    
        # Important: initialize the super class
        super(Resonator_LC_Lumped, self).__init__()
    
        # declare the parameters
        self.param("l", self.TypeLayer, "Layer", default = pya.LayerInfo(1, 0)) 
        self.param("w", self.TypeDouble, "Trace Conductor Width", default = 5)
        self.param("s", self.TypeDouble, "Capacitor Fingers Separation", default = 5)
        self.param("n", self.TypeInt, "Number of Capacitor Fingers", default = 17) 
        self.param("c_l", self.TypeDouble, "Capacitor Fingers Length", default = 320)   
        self.param("length", self.TypeDouble, "Inductor Length", default= 1000)
    
      def display_text_impl(self):
        # Provide a descriptive text for the cell
        return "LC Res( Inductor Length=" + ('%.3f' % self.length) + ")"
    
    
      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 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
        w_dbu = self.w / self.layout.dbu
        length_dbu = self.length / self.layout.dbu
        s_dbu = self.s / self.layout.dbu
        c_l_dbu = self.c_l / self.layout.dbu
    
        # compute the points assoicated to both tracks
        #Note this is for positive resist and etching after lithography
        cap_track = []
    
    
        #points associated with the track for the capacitor
        pt1 = []#[(-length_dbu,w_dbu), (length_dbu, w_dbu)]
    
        #centering this about zero on the x-axis
        cx1, cx2 = [-1*c_l_dbu/2 , c_l_dbu/2] #x coordinates of the meandering trace for the capacitor
    
        #this loops cycles through all terms until a desired of capacitors fingers are achieved
        initial_offset = -s_dbu/2
        y_cor = initial_offset
    
        for idx, value in enumerate(cycle([cx2, cx1])):
    
          for i in range(2):
    
            x_cor = value
            y_cor +=  -i * (s_dbu + w_dbu)
    
            pt1.append((x_cor, y_cor))
    
          if idx == self.n - 1:  # Only loop through the number of capacitor fingers required
            break
    
    
    
        for i in pt1:
          cap_track.append(pya.Point.new(pya.DPoint(i[0],i[1])))
    
    
        #initializing the meandering path
        special_path = pya.Path(cap_track, s_dbu)
    
        #setting attributes for the object (begin and end extension)
        special_path.end_ext =  s_dbu/2
        special_path.round = True
        special_path.bgn_ext =  s_dbu/2
    
    
        # create the shape
        self.cell.shapes(self.l_layer).insert(special_path)
    
  • That is to say even when I use the exact same snippet of code I am using, the path shown in KLayout remains unchanged, regardless of the geometry of the path.

  • @nsrgorgi You can format source code using a single line with three backticks before and after the code. Without that it's hardly readable. It's the same notation GitHub Markdown uses for example. I took the liberty to edit your text.

    I just checked your code and although there is some room for improvement, it correctly configures the path:

    The path has round ends and the extensions are half the width of the path. So what is your expectation?

    The code has some flaws, like you use "s_dbu" to configure the path width when you obviously mean "w_dbu". In general you could build the Point array directly in the loop rather than generating tuples first and then converting them into point objects. By using DPoint and DPath objects you can directly work with micrometer units and do not have to convert to DBU space by dividing by the DBU.

    This "produce_impl" is my version that should fix these flaws:

      def produce_impl(self):
    
        #points associated with the track for the capacitor
        pts = []
    
        #centering this about zero on the x-axis
        cx1, cx2 = [-1*self.c_l/2 , self.c_l/2] #x coordinates of the meandering trace for the capacitor
    
        #this loops cycles through all terms until a desired of capacitors fingers are achieved
        y_cor = -self.s/2
    
        for idx, value in enumerate(cycle([cx2, cx1])):
    
          for i in range(2):
    
            x_cor = value
            y_cor += -i * (self.s + self.w)
    
            pts.append(pya.DPoint(x_cor, y_cor))
    
          if idx == self.n - 1:  # Only loop through the number of capacitor fingers required
            break
    
        #initializing the meandering path
        special_path = pya.DPath(pts, self.w)
    
        #setting attributes for the object (begin and end extension)
        special_path.end_ext = self.w/2
        special_path.bgn_ext = self.w/2
        special_path.round = True
    
        # create the shape
        self.cell.shapes(self.l_layer).insert(special_path)
    

    Matthias

Sign In or Register to comment.