1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

GDSIIファイルを gdstk と Shapely を使って設計する方法

Last updated at Posted at 2023-12-12

はじめに

この記事では、GDSIIファイルを gdstk だけで生成するのではなく、 Shapely の機能を使って簡単に設計する方法を紹介します。

gdstk の使い方については、

などで紹介したのでそちらを参考にしてください。

ここでは、gdstk を使うよりも、Shapely という GEOSをベースとしたpythonライブラリで、ジオメトリの操作および分析のために使われます。Shapely を使うと、簡単にジオメトリの重なりや、面積なども計算できるので、Shapelyを使ってジオメトリを生成し、それを gdstk を用いて GDSファイルに変換する方法を紹介します。

この記事は、google colab でも実行できるように、

を用意してあります。

可変長の配線幅を生成したいときは、

を参考にしてください。

前準備

gtdtk の install

詳細は、

を参考にしてください。

ここでは、google colab の例だけ紹介しておきます。

condacolab の install
# install condacolab
!pip install -q condacolab
import condacolab
condacolab.install() # Mambaforge-23.1.0-1-Linux-x86_64.sh...  @2023.11.30
# check conda version
!conda --version # conda 23.1.0
gdstk の install
# Create a new conda environment named gdstk
!conda create -n gdstk -c conda-forge --strict-channel-priority
# Activate the new environment
!conda activate gdstk
# Install gdstk
!conda install gdstk

必要なモジュールの import

import matplotlib.pyplot as plt
import numpy as np
from IPython.display import *
import gdstk
import shapely.geometry as geometry

print("gdstk.__version__ = ", gdstk.__version__)

gdstk._version_ = 0.9.47 (2023.11.29) を用いた例になります。

GDS ファイルを matplotlib で表示する関数

これも詳細は別の記事 を参考にしてください。ここでは、 gdsファイルを matplotlib で表示する関数の定義だけ紹介しておきます。

def plot_gds_onlypoly(gdsfile, transformation=None, maxlayer = 10, margin=3, debug=False):
  lib = gdstk.read_gds(gdsfile)
  top_cells = lib.top_level()
  fig, ax = plt.subplots(1, 1, figsize=(8,8), tight_layout=True)
  cmap = plt.get_cmap('viridis', maxlayer)
  # すべてのトップレベルセルのポリゴンをプロット
  for cell in top_cells:
    (xmin,ymin),(xmax,ymax) = cell.bounding_box()
    if hasattr(cell, 'polygons'):
      print("polygons in cell is found. length = ", len(cell.get_polygons()))
      for polygon in cell.get_polygons(): # cell.polygons does not work when the reference is used.
        points = np.array(polygon.points)
        if debug: print("     layer = ", polygon.layer, " area = ", polygon.area())
        ax.plot(*np.vstack((points, points[0])).T, linestyle='solid', linewidth = 1, alpha=0.8, color=cmap(polygon.layer))

  ax.set_title("polygons")
  ax.set_xlim(xmin - margin, xmax + margin)
  ax.set_xlim(xmin - margin, xmax + margin)
  ax.set_aspect('equal', adjustable='box')
  ax.grid(alpha=0.3)
  plt.savefig(gdsfile.replace(".gds",".png"))
  plt.show()

Shapely を用いて多画素のジオメトリを生成する関数

まず、関数 create_multi_pixel_array を作成します。多数のピクセルを含む配列の幾何学的レイアウトを生成し、それを可視化するために設計されています。関数は shapely ライブラリを使用して幾何学的オブジェクトを生成し、matplotlib を使用して結果を描画します。以下は各部分の詳細です。

関数の定義

  • num_rows, num_cols: 配列の行数と列数。
  • pad_size, pixel_size: パッドとピクセルのサイズ。
  • wire_width, wire_length: ワイヤの幅と長さ。
  • spacing: ピクセル間のスペース。

リストの初期化:

pads, pixels, wires というリストを初期化して、パッド、ピクセル、ワイヤの幾何学的形状を格納します。

    # Initialize lists for pads, pixels, and wires
    pads, pixels, wires = [], [], []

全体的なレイアウトサイズの計算:

配列の全幅と全高を計算します。

    # Calculate total layout size
    total_width = num_cols * (pixel_size + spacing)
    total_height = num_rows * (pixel_size + spacing)

各ピクセルの幾何学的形状の生成:

二重ループを使用して各ピクセルの位置を計算し、shapely.geometry.box と shapely.geometry.LineString を使用してピクセル、パッド、ワイヤの形状を生成します。

    # Create geometry for each pixel in the array
    for i in range(num_rows):
        for j in range(num_cols):
            # Calculate positions
            x_position = (j - num_cols / 2) * (spacing) + spacing / 2
            y_position = (i - num_rows / 2) * (spacing) + spacing / 2
...

美しい書き方ではないかもしれませんが、、この for loop の中でジオメトリを生成します。

            wire_left = wire_left.buffer(wire_width, cap_style="flat")
            wire_right = wire_right.buffer(wire_width, cap_style="flat")

この操作により、LineString という幅のないオブジェクトから、幅のある Polygon に変換します。配線は一次元の紐で生成してから、幅をもたせる、という作戦です。cap_style はデフォルトで round で丸みを持った形状になりますので、ここでは flat にして、特に余計なことはしない、という設定にしてあります。

matplotlibを使用した描画:

plt.subplots で描画エリアを作成し、ax.fill と ax.plot を使用してパッド、ピクセル、ワイヤを描画します。

    # Plotting with matplotlib
    fig, ax = plt.subplots()
    for pad in pads:
        x, y = pad.exterior.xy
        ax.fill(x, y, alpha=0.5, ec='none')
    for pixel in pixels:
        x, y = pixel.exterior.xy
        ax.fill(x, y, alpha=0.5, ec='none', fc='blue')
    for wire in wires:
        x, y = wire.exterior.xy
        ax.plot(x, y, linewidth=wire_width, color='black')

padspixels は fill で中身を塗りつぶして、wires という配線を想定したオブジェクトは plot で線で描画しています。

戻り値

関数は、生成されたパッド、ピクセル、ワイヤのリストを返します。

この関数は、電子工学やマイクロエレクトロニクスの分野で、微細な電子回路やセンサーアレイの設計の簡易版をイメージして作成しています。

下記、def create_multi_pixel_array の全文になります。

def create_multi_pixel_array(num_rows, num_cols, pad_size, pixel_size, wire_width, wire_length, spacing):
    """
    Create a geometry layout for a multi-pixel array setup with shapely and plot it with matplotlib.
    """

    # Initialize lists for pads, pixels, and wires
    pads, pixels, wires = [], [], []

    # Calculate total layout size
    total_width = num_cols * (pixel_size + spacing)
    total_height = num_rows * (pixel_size + spacing)

    # Create geometry for each pixel in the array
    for i in range(num_rows):
        for j in range(num_cols):
            # Calculate positions
            x_position = (j - num_cols / 2) * (spacing) + spacing / 2
            y_position = (i - num_rows / 2) * (spacing) + spacing / 2

            # Create pixel
            pixel = geometry.box(x_position - pixel_size / 2, y_position - pixel_size / 2,
                                 x_position + pixel_size / 2, y_position + pixel_size / 2)
            pixels.append(pixel)

            # Create pads for each pixel
            pad_left = geometry.box(x_position - pad_size / 2, y_position - pad_size/2 - pixel_size/2 - wire_length/2,
                                    x_position + pad_size / 2, y_position + pad_size/2 - pixel_size/2 - wire_length/2)
            pad_right = geometry.box(x_position - pad_size / 2, y_position - pad_size/2 + pixel_size/2 + wire_length/2,
                                     x_position + pad_size / 2, y_position + pad_size/2 + pixel_size/2 + wire_length/2)
            pads.extend([pad_left, pad_right])

            # Create wires for each pixel
            wire_left = geometry.LineString([(x_position, y_position - pixel_size / 2), (x_position, y_position - pixel_size / 2 - wire_length/2)])
            wire_right = geometry.LineString([(x_position, y_position + pixel_size / 2), (x_position, y_position + pixel_size / 2 + wire_length/2)])
            wire_left = wire_left.buffer(wire_width, cap_style="flat")
            wire_right = wire_right.buffer(wire_width, cap_style="flat")

            wires.extend([wire_left, wire_right])

    # Plotting with matplotlib
    fig, ax = plt.subplots()
    for pad in pads:
        x, y = pad.exterior.xy
        ax.fill(x, y, alpha=0.5, ec='none')
    for pixel in pixels:
        x, y = pixel.exterior.xy
        ax.fill(x, y, alpha=0.5, ec='none', fc='blue')
    for wire in wires:
        x, y = wire.exterior.xy
        ax.plot(x, y, linewidth=wire_width, color='black')

    ax.set_aspect('equal', adjustable='box')
    plt.savefig("geom_"+str(num_rows) + "_" + str(num_cols) + ".png")
    plt.show()

    return pads, pixels, wires

実行例

いよいよ準備が整ったので実行してみましょう。

1x2 画素の例

now_rows と num_cols を 1, 3 に設定し、下記のパラメータで画素を生成してみます。

# Example parameters
num_rows = 1    # Number of rows in the pixel array
num_cols = 3    # Number of columns in the pixel array
pad_size = 4   # Size of the bonding pads
pixel_size = 12.0 # Size of each pixel
wire_width = 1 # Width of the wires
wire_length = 10  # Length of the wires
spacing = 30.0    # Spacing between pixels

# Create and plot the geometry
pads, pixels, wires = create_multi_pixel_array(num_rows, num_cols, pad_size, pixel_size, wire_width, wire_length, spacing)

これを実行すると、次のような3つの画素が生成されます。

output_15_0.png

10 x 5 画素の例

次に、now_rows と num_cols を 5, 5 に設定し、25画素の例を作成してみます。

# Example parameters
num_rows = 5    # Number of rows in the pixel array
num_cols = 5    # Number of columns in the pixel array
pad_size = 4   # Size of the bonding pads
pixel_size = 12.0 # Size of each pixel
wire_width = 1 # Width of the wires
wire_length = 10  # Length of the wires
spacing = 30.0    # Spacing between pixels

# Create and plot the geometry
pads, pixels, wires = create_multi_pixel_array(num_rows, num_cols, pad_size, pixel_size, wire_width, wire_length, spacing)

これを実行すると、5x5画素のジオメトリが生成されます。

output_17_0.png

gdstk で GDSII 形式に書き出して保存し、matplotlib で確認

このようにして、Shapelyでジオメトリを作成して、問題なければ、gdstk を用いて GDSII形式に書き出してみましょう。

ここも詳細は別の記事 を参考にしてください。

# LSI回路のジオメトリを作成
lib = gdstk.Library()
cell = lib.new_cell("IMAGER_ARRAY")

tag = {
    "pixel": {"layer": 1, "datatype": 1},
    "wire": {"layer": 5, "datatype": 5},
    "pad": {"layer": 10, "datatype": 10},
}

# Convert shapely geometries to gdstk polygons
for pad in pads:
    cell.add(gdstk.Polygon(pad.exterior.coords, **tag["pad"]))
for pixel in pixels:
    cell.add(gdstk.Polygon(pixel.exterior.coords, **tag["pixel"]))
for wire in wires:
    cell.add(gdstk.Polygon(wire.exterior.coords, **tag["wire"]))

# Save the GDS file
outgdsfile="image_array.gds"
lib.write_gds(outgdsfile)

# GDSファイルに保存
lib.write_gds(outgdsfile)
plot_gds_onlypoly(outgdsfile, debug=False)

これを実行すると、

output_19_1.png

このように、 gdsファイルから次のようなジオメトリが生成されます。

gdsファイルを KLayout で見ると、

スクリーンショット 2023-12-12 23.18.25.png

のように見えます。

最後に

gdstk を生で動かしてジオメトリを描くよりも、Shapely を使った方が便利かなぁと思って、この記事を書いてみました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?