Parallel paths

edited February 2018 in KLayout Development
Hi Matthias,

Would it be easy and feasible over time to add a new "path" drawing function which draws several paths in parallel at once, with parameters being the number of paths in parallel, the width of the parallel paths and the space in between the parallel paths? An extra parameter could be shortening the parallel paths each x um. Some technologies have a tough constraint on maximum width forcing one to draw high-current paths as parallel paths.

_____________ ____________ ____________ ____________ ____________ ________________
|____________|____________|____________|____________|____________|_______________|
|____________|____________|____________|____________|____________|_______________|
|____________|____________|____________|____________|____________|_______________|
|____________|____________|____________|____________|____________|_______________|


Cheers,

Tomas

Comments

  • edited February 2018

    Even better, it's already there: you can create a PCell for this.

    Here is a first version - without the shorting yet. It will plug itself into the Basic Library. You can draw a wide path and turn it into a "Basic.SPLIT_PATH" PCell using "Edit/Selection/Convert Shape to PCell. The original path will still be there, but on a non-layout layer. If you modify this path, the generated split path will follow the original one. If you make it wider, the path will be resolved into more splits.

    The shorting is probably easy to implement, but some explanation is required how to they should look like at the corners.

    Matthias

    # Split-path PCell
    #
    # This PCell will take a path and split it into multiple parallel
    # ones. The PCell adds itself as SPLIT_PATH to the Basic library.
    
    # Remove any definition of our classes (this helps when 
    # reexecuting this code after a change has been applied)
    Object.constants.member?(:SplitPathPCell) && Object.send(:remove_const, :SplitPathPCell)
    
    # The PCell declaration for the split path
    class SplitPathPCell < RBA::PCellDeclarationHelper
    
      include RBA
    
      def initialize
    
        # Important: initialize the super class
        super
    
        # declare the parameters
        param(:l, TypeLayer, "Layer", :default => LayerInfo::new(1, 0))
        param(:p, TypeShape, "Path", :default => DPath::new([ DPoint::new(0, 0), DPoint::new(10.0, 10.0) ], 1.0))
        param(:w, TypeDouble, "Width of splits", :default => 0.15)
        param(:s, TypeDouble, "Separation of splits", :default => 0.1)
    
      end
    
      def display_text_impl
        # Provide a descriptive text for the cell
        "SPLIT_PATHH(L=#{l.to_s},W=" + ("%.12g" % w) + ",S=" + ("%.12g" % s) + ")"
      end
    
      def can_create_from_shape_impl
        # Implement the "Create PCell from shape" protocol: we can use any shape which 
        # is a path
        shape.is_path?
      end
    
      def parameters_from_shape_impl
        # Implement the "Create PCell from shape" protocol: we set the path and the layer
        # from the selected shape
        set_p shape.dpath
        set_l layout.get_info(layer)
      end
    
      def produce_impl
    
        n = ((p.width - w) / (w + s) + 1e-6).floor.to_i
        # limit to 1 .. 200
        n = [ [ n, 200 ].min, 1 ].max
    
        (n + 1).times do |i|
    
          # this is the shift distance    
          d = (i - n * 0.5) * (w + s)
    
          # gets the points and eliminate duplicate ones
          pts = []
          ip = 0
          p.each_point do |pt|
            if ip == 0 || pt != pts[-1]
              pts << pt
            end
            ip += 1
          end
    
          # create the shifted points      
          pts_new = []
          if d.abs < 1e-6
            # shortcut for the center line
            pts_new = pts
          elsif pts.empty?
            # skip empty paths
          elsif pts.size == 1
            pts_new << pts[0] + RBA::DVector::new(0.0, d)
          else
    
            left_side = d < 0.0
            d = d.abs
    
            pts.size.times do |ip|
    
              if ip == 0
                va = vb = pts[ip + 1] - pts[ip]
              elsif ip == pts.size - 1
                va = vb = pts[ip] - pts[ip - 1]
              else
                va = pts[ip] - pts[ip - 1]
                vb = pts[ip + 1] - pts[ip]
              end
    
              if left_side
                va, vb = -vb, -va
              end
    
              pt = pts[ip]
              va = va * (1.0 / va.length)
              vb = vb * (1.0 / vb.length)
              vv = (va - vb)
    
              # compute the new point. Most cases are to avoid extreme 
              # delocations in case of actute outer or inner corner
              if va.vprod_sign(vb) == 0
                if va.sprod_sign(vb) > 0
                  pts_new << pt + (RBA::DTrans::R90 * va) * d
                else
                  if !left_side
                    pts_new << pt + (RBA::DTrans::R90 * va + va) * d
                  end
                  pts_new << pt + (RBA::DTrans::R90 * vb - vb) * d
                  if left_side
                    pts_new << pt + (RBA::DTrans::R90 * va + va) * d
                  end
                end
              else
                l = d * va.sprod(vv) / va.vprod(vv)
                if l > d || l < -d * 2
                  if !left_side
                    pts_new << pt + (RBA::DTrans::R90 * va + va) * d
                  end
                  pts_new << pt + (RBA::DTrans::R90 * vb - vb) * d
                  if left_side
                    pts_new << pt + (RBA::DTrans::R90 * va + va) * d
                  end
                else
                  pts_new << pt + (RBA::DTrans::R90 * va * d + va * l)
                end
              end
    
            end
    
          end
    
          # Creates the new shifted path
          pp = p.dup
          pp.width = w
          pp.points = pts_new
          cell.shapes(l_layer).insert(pp)
    
        end
    
      end
    
    end
    
    basic_lib = RBA::Library::library_by_name("Basic")
    if basic_lib
      # Create the PCell declaration
      basic_lib.layout.register_pcell("SPLIT_PATH", SplitPathPCell::new)
    end
    
  • edited November -1
    Hi Matthias,

    It works like a charm! I didn't know it already existed, or at least I could not find it back, but the use is similar to the ROUND_PATH Pcell which is available by default. The corners can indeed be tricky. I can think of 2 ways but both are not ideal:

    1) for orthogonal routing the horizontal and vertical lines could all cross in the corner (like a grid), but this means a local higher density.
    2) lines keep being parallel (like "L"'s) in the corners but this could mean that the inner line sections could be much less resistive than the outer ones between two shortings which is not ideal either (unless you have two corners in between the shorting lines).

    I tend to go for option one mostly, but it would be great to have both option available over time.

    The idea of having the shorts is not to loose the full-length of each of the parallel lines that would have a process-related interruption.

    Thanks again for the great support and tool!

    Cheers,

    Tomas
Sign In or Register to comment.