Create PCell successively by macro code

Hello,

I have several PCell classes in one script, and many of them are connected by many parameters.
What I wnat to do is that, as I create one PCell, the next one would be created, and the next , and the next...
For example, if I have A, B, and C PCell classes.
B PCell would be created after A is created, and C PCell would be created after B is created.

I tired to implement this by adding

endInside = EndInside_nJFET::new
endInside.produce_impl

at the end of "produce_impl" method but it did not work.

How can I implement this ?

Thank you very much :)

Comments

  • edited June 2020

    You mean "tried", not "tired"? Those mean quite different things :)

    "produce_impl" is not a regular function that is not called when you expect it. It runs in a kind of sandbox in an entirely different context. If you call it yourself, the effect will be unpredictable.

    Instead, you should organize the code in classes or functions which provide the production methods separated from the PCell classes. Then you delegate to these objects or functions.

    Here is what I mean:

    def PCellA_produce_impl(self, context)
       # production code from your PCellA, but instead of using "layout", "cell", "param_x" etc.
       # you say "context.layout", "context.cell", "context.param_x" etc.
    end
    
    def PCellB_produce_impl(self, context)
       # production code from your PCellB ...
       ...
       # here is how you call PCellA's production code
       PCellA_produce_impl(context)
    end
    
    class PCellA
      def produce_impl(self)
         PCellA_produce_impl(self)
      end
    end
    
    class PCellB
      def produce_impl(self)
         PCellB_produce_impl(self)
      end
    end
    

    Matthias

  • edited July 2020

    Thank you very much Matthias :)

    I tried this way, but I got errors,

    firstly, I followed how you did, but I got an error becuase of "self" in PCellA_produce_impl method.
    So, I removed the "self" from the method( now, only context is a parameter), then got another error becuase of "self" in produce_impl.
    So, I removed the "self" from the mothod, then another error occurec says, undfined method "PCellA_produce_impl".

    I think this is because "PCellA_produce_impl" is outside of class "PCellA".

    The final goal of my project is creating a device automatically as you call one PCell class.

    detail description below.
    I have Unit cells which I currently have.
    These Unit cells are implemented in one Module and each single class like class CellA, class CellB ...
    Then, as I put many unit cells together, I will make a cluster. So, one cluster consists of many unit cells.
    Finally, as I put many clusters together, I will make one device.

    Below, there is simplified code of unit cell module.

    Basically, there are two unit cells which are "CellA" and "CellB" in a module "UnitCellLibrary"
    Firstly, you have to call CellA, because there are global variables, and as you call CellA, global variables will be set.
    With those global variables, parameters in CellB will be set when user call CellB.

    module UnitCellLibrary
      include RBA
      UnitCellLibrary.constants.member?(:UnitCellLibrary) && remove_const(:UnitCellLibrary)
      UnitCellLibrary.constants.member?(:CellA) && remove_const(:CellA)
      UnitCellLibrary.constants.member?(:CellB) && remove_const(:CellB)
      # in order to feed parameter value from CellA to CellB, I used global variable as a method.
      $global_variable_A = 0.0
      $global_variable_B = 0.0
    
    class CellA < PCellDeclarationHelper
        include RBA 
    
        def initialize
          super      
          # inputA and inputB are user input. 
          param(:inputA, TypeDouble, "inputA", :default => 1.5, :unit => "µm")
          param(:inputB, TypeDouble, "inputB", :default => 1.0, :unit => "µm")      
          # paramA and paramB will be set after user input inputA and inputB
          param(:paramA, TypeDouble, "paramA", :default => 0.0, :unit => "µm", :hidden => true)
          param(:paramB, TypeDouble, "paramB", :default => 0.0, :unit => "µm", :hidden => true)
        end 
    
        def display_text_impl
          "CellA"
        end 
    
        def coerce_parameters_impl
          # Set parameters by calling function
          paramA_set
        end    
    
        def produce_impl
          app = RBA::Application.instance
          mw = app.main_window
          lv = mw.current_view
          raise "No view selected" if lv.nil?
          dbu = lv.active_cellview.layout.dbu  
          # Set Global variables
          # these global variables will be used in class CellB
          $global_variable_A = paramA
          $global_variable_B = paramB     
    
          # Generate shapes
          # lb_u => Left Below point of upper PCell // ru_u => Right Upper point of upper PCell
          # lu_l => Left Upper point of Lower PCell // rb_l => Right Below point of Lower PCell
          blockA_x_lb_u = 0
          blockA_y_lb_u = 0
          blockA_x_ru_u = paramA
          blockA_y_ru_u = paramB
    
          # Generate layer shapes 
          shape_1000 = cell.shapes(layout.layer(1000, 0))
    
          # Placing shapes 
          block = RBA::Region::new(RBA::Box::new(blockA_x_lb_u / dbu, blockA_y_lb_u / dbu, blockA_x_ru_u / dbu, blockA_y_ru_u / dbu))    
          shape_1000.insert(block)      
        end
    
        def paramA_set
          set_paramA(inputA + 10)      
          paramB_set
        end
    
        def paramB_set
          set_paramB(inputB + 10)
        end    
      end
    
    class CellB < PCellDeclarationHelper
        include RBA
    
        def initialize
          super 
          # paramA and paramB will be set based on the global variable from CellA  
          param(:paramA, TypeDouble, "paramA", :default => 0.0, :unit => "µm", :hidden => true)
          param(:paramB, TypeDouble, "paramB", :default => 0.0, :unit => "µm", :hidden => true)
        end
    
        def display_text_impl
          # Provide a descriptive text for the cell
          "CellB"
        end
    
        def coerce_parameters_impl
          # Set geometries as global parameters
          paramA_set
        end
    
        def produce_impl
          app = RBA::Application.instance
          mw = app.main_window
          lv = mw.current_view
    
          raise "No view selected" if lv.nil?
    
          dbu = lv.active_cellview.layout.dbu      
          # Generate layer shapes 
          shape_2000 = cell.shapes(layout.layer(2000, 0))
    
          # Coordintatin Box points
          # lb => left bottom point
          # ru => right upper point 
          # metalSource
          blockB_x_lb_u = 0
          blockB_y_lb_u = 0
          blockB_x_ru_u = paramA
          blockB_y_ru_u = paramB
    
          # Creating boxes and Placing shapes #
          block = RBA::Region::new(RBA::Box::new(blockB_x_lb_u / dbu, blockB_y_lb_u / dbu, blockB_x_ru_u / dbu, blockB_y_ru_u / dbu))
          shape_2000.insert(block)
        end
    
        def paramA_set
          set_paramA($global_variable_A + 10)      
          paramB_set
        end
    
        def paramB_set
          set_paramB($global_variable_B + 10)
        end
      end
    
      # The library where we will put the PCell into
      class UnitCellLibrary < Library
        def initialize
          # Set the description
          self.description = "Cell Library"
          # Create the PCell declarations
          layout.register_pcell(" CellA", CellA::new)
          layout.register_pcell(" CellB", CellB::new)      
          register("Cell")
        end
      end
    
      # Instantiate and register the library
      UnitCellLibrary::new
    end
    

    These are cells I called. Left orange cell is CellA, and the other is CellB

    So now, I have two cells, and I want to make a cluster consisting of CellA and CellB like this by calling another class like "clusterA"

    Therefore, clusterA calls CellA and CellB, then arrange them like the figure.

    Sometimes, when other cluster class arranges unit cells, rotation or flip might be needed. (not in this case but for other cluster class and actual unit cells consist of many boxes, so using only "transposed" might not work for this case)

    How can I implement this with Ruby in Klayout or is it possible to implement?

    Thank you very much for reading long question.

  • Hi,

    My example was Python, hence the "self".

    For a discussion of how to call a PCell inside another one, please see: https://www.klayout.de/forum/discussion/comment/5948#Comment_5948

    This problem has been addressed a couple of times here already. I usually say you should not do this as creating a PCell instance inside another PCell is just a very complicated way of calling that other PCell's production code. It may be acceptable if you need to create arrays of PCells because of the compactness of array calls.

    Matthias

Sign In or Register to comment.