pythonでGDSファイル形式のデバイスレイアウトを描く方法

# はじめに
この記事は[eeic Advent Calendar 2017](https://qiita.com/advent-calendar/2017/eeic)の2日目の記事です。

eeicとは東京大学工学部電気電子/電子情報工学科の通称でして、
Advent Calendarのページには

>半導体とか情報工学とかエネルギーとか制御工学とか、電気にまつわる幅広い分野を研究している学科です。

とあります。
しかしこのようなイベントがあるとどうしても参加記事が情報系に偏ってしまいがちなため、少しばかりでもデバイス系成分を添えようと思いこの記事を書いております。

この記事ではLSIなど半導体デバイスに用いられるレイアウトファイルをCADソフトウェアではなくpythonのモジュールを使って書く方法をご紹介します。
ごく一部の人にしか需要がない内容だと思いますがご容赦ください。
# GDSファイルとは
GDSファイル、正式名称GDSIIファイルは、半導体業界で標準的に用いられている半導体レイアウトデータのファイルです。
詳しくは[wikipediaの記事](https://en.wikipedia.org/wiki/GDSII)でも見てください。ていうか、GDSファイルなんて知らないよという人はここから先の記事を読む必要はないでしょう。
デバイスのレイアウト設計をする際、普通はGUIの半導体用CADツールを用いて設計するかと思います。

# gdsCAD
gdsCADとは、先ほど説明しましたGDSファイルを作成することのできるPythonのモジュールです。
コードでデバイスのレイアウトを書くことができるため、GUIのCADソフトで書くのが気が遠くようなパターンをこれを用いるとすごく簡単に描ける、なんてこともあると思います。
詳しくは[公式ドキュメント](http://pythonhosted.org/gdsCAD/)をご覧ください。

 
# 準備
まずgdsCADの動く環境を用意します。
最初に、gdsCADはPython3ではなくPython2でしか動きません。Python2の環境を用意しましょう。
macだとデフォルトで入っていると思います。Windowsの人はAnacondaなり、Bash on Ubuntu on Windowsで入れれば良いのではないでしょうか。Pythonのインストールはググれば詳しい記事が腐る程出てくると思います。
gdsCADはPyPIからインストールできます。
私と同じmacOS Sierra 10.12.6の環境ではPythonのインストールから準備すると、

```
brew install python
pip2.7 install gdsCAD
pip2.7 install numpy
```

となります。numpyという配列計算のモジュールも必要なのでなければ一緒にインストールしてください。
作成したレイアウトをディスプレイするのにmatplotlibなどのモジュールのインストールする必要があるみたいですが,今回は用いないので必要なら適宜pipでインストールしてください（必要なパッケージは[ここ](http://pythonhosted.org/gdsCAD/)を参考に。）

# レイアウトを描いてみる
まずgdsCADを用いたプログラムの基本的な書き方を説明するために、いくつか簡単な図形を出力するプログラムを見て見ましょう。

``` python:gdsCAD_example01.py
# -*- coding: utf-8 -*-
import numpy as np
from gdsCAD import *

def main():
    # creating elements to draw
    rectangle = shapes.Rectangle((-50,-30),(-30,30))
    circle = shapes.Disk((40,0),20,layer=1)
    text = shapes.Label("TSV",30,(-45,40),layer=2)
    box = shapes.Box((-80,-80),(80,80),5,layer=3)

    # creating a Cell to hold the elements
    cell = core.Cell('TOP')
    cell.add([rectangle,circle,text,box])
    
    # editting elements
    text2 = shapes.Label("MIRROR",20,(-60,-60),layer=2)
    text2.reflect('x',(0,-50))
    cell.add(text2)

    cell.add(utils.rotate(rectangle,60,(-40,0)))

    # adding cell to a Layout and save
    layout = core.Layout('pattern')
    layout.add(cell)
    layout.save('output.gds')

    #layout.show()

if __name__ == "__main__":
    main()
```

このコードを```python gdsCAD_example01.py```とコマンドラインで実行しすると、出力ファイル```output.gds```ができています。

gdsファイルに対応しているソフトウェアで開くと、次の画像のレイアウトになっているのを見ることができます。
上のソースコードで最後にコメントアウトされている```layout.show()```によって見られるはずなのですがうまく出力できなかったので、今回はフリーソフトである[KLayout](https://www.klayout.de/)を用いて表示しました。
<img width="814" alt="output1.png" src="https://qiita-image-store.s3.amazonaws.com/0/98279/eb00cb73-8429-2f7e-105e-ee163e4118bf.png">

# 基本的な書き方
gdsCADの部分の基本的な書き方は、以下の3手順になります。
1.　図形を作成する。
2.　作成した図形をCellに追加する。
3.　CellをLayoutに追加して、gdsファイルに出力します。


Cellはデバイスレイアウトの一部のブロック、Layoutはデバイスレイアウト全体のまとまり、だと思ってください。
よくわからなくても上のコードや公式ドキュメントの[テンプレート](http://pythonhosted.org/gdsCAD/examples.html)を真似すればなんとなくで書けます。
それでは先ほどのコードを順に解説していきます。

``` python
import numpy as np
from gdsCAD import *
```

gdsCADとnumpyをインポートします。上のコードではnumpyは使っていませんが、おまじないだと思って書きましょう。```def main():``` 
のあたりが分からない人は、python実行ファイルのテンプレートなのでそのまま真似してください。




``` python
# creating elements to draw
    rectangle = shapes.Rectangle((-50,-30),(-30,30))
    circle = shapes.Disk((40,0),20,layer=1)
    text = shapes.Label("TSV",30,(-45,40),layer=2)
    box = shapes.Box((-80,-80),(80,80),5,layer=3)
```

shapesのクラスを用いると、円や正方形など基本的な図形を描くことができます。
このコードでは、上の行から順に正方形、円、テキストの図形、四角の枠を描画しています。
引数は関数によって異なりますが、例えば3行目の円を描画している行では第一引数で中心の座標、第二引数で半径を指定しています。
引数で```layer=1```と指定することで、図形のレイヤー分けも指定できます。

他の図形を描画する関数や各関数の詳しい引数がちゃんと知りたければ[ここ](http://pythonhosted.org/gdsCAD/api/shapes_api.html)を探してください。


``` python
# creating a Cell to hold the elements
    cell = core.Cell('TOP')
    cell.add([rectangle,circle,text,box])
```

次に先ほどの手順2にあたる、Cellの作成です。
ここでは'TOP'という名前のcellを作成して、そのcellに先ほど作成した4つの図形要素を追加しています。

``` python
    # editting elements
    text2 = shapes.Label("MIRROR",20,(-60,-60),layer=2)
    text2.reflect('x',(0,-50))
    cell.add(text2)

    cell.add(utils.rotate(rectangle,60,(-40,0)))
```

gdsCADでは回転や反転などの図形の変形を行うことももちろん可能です。
上のコードではふた通りのやり方をしていますが、上のコードでの```reflect```、反転操作のような作成した図形のクラスの関数を用いて変形を行うやり方と、
```rotate```、回転変形のように```utils```クラスの関数を用いて、図形を引数にとってやるやり方があります。
基本的にどちらでも大丈夫だと思うので好きな方を用いてください。

どんな操作ができるかは、詳しくは先ほどと同じ[APIのページ](http://pythonhosted.org/gdsCAD/api/utils_api.html#module-gdsCAD.utils)で探して見てください。


``` python
    # adding cell to a Layout and save
    layout = core.Layout('Pattern')
    layout.add(cell)
    layout.save('output.gds')
```
最後に先ほどの手順の最後、3番目のcellをlayoutにまとめる部分です。
ここでは'Pattern'というラベルを付けたLayoutを作成して先ほど作成した図形を含むCellを加えています。
そしてsave関数で指定のgdsファイルに出力すれば完了です。

以上、が基本的なプログラムの流れです。よく分からなかったかもしれませんが、
今回のコードや[公式のサンプルコード](http://pythonhosted.org/gdsCAD/examples.html)などを見ながら一度何かを描いてみればすぐマスターできると思います。

# 複雑な図形の描画
ここまでは各図形を描画する関数を用いる方法のみを紹介しましたが、そんなのGUIでもできるやん、と思った人も多いかと思います。
基本図形でない複雑な形のパターンの描画には、境界線の頂点の配列からBoundary関数を用いることで描画できます。Boundary関数は、与えられた点の配列によって囲われた領域を図形として得る関数です。
Boundary関数を用いたパターンの描画例を載せます。一部[こちら](http://pythonhosted.org/gdsCAD/userguide.html)のコードを利用しました。

``` python:gdsCAD_example02.py
# -*- coding: utf-8 -*-

import numpy as np
from math import *
from gdsCAD import *

def main():
    #drawing random polygon
    points = np.random.rand(20,2) * 20
    bdy = core.Boundary(points)

    # drawing a triangle
    points2 = [(25,0),(45,0),(35,20)]
    triangle = core.Boundary(points2)

    # drawing hexagon
    N = 6
    length = 10.0
    points3 = np.zeros((6,2))
    for n in range (0,N):
        points3[n]=[length * cos(2.0 * pi * n / N), length * sin(2.0 * pi * n / N)]
        points3[n] += [60,10]

    polygon = core.Boundary(points3)
    
    # creating a Cell to hold the elements
    cell = core.Cell('TOP')
    cell.add([bdy,triangle,polygon])

    # add cell to a Layout and save
    layout = core.Layout('pattern')
    layout.add(cell)
    layout.save('output2.gds')


if __name__ == "__main__":
    main()

```
出力はこのようになります。
<img width="450" alt="output2.png" src="https://qiita-image-store.s3.amazonaws.com/0/98279/2fee75e9-e1f9-605c-11bb-ba0927b1edf8.png">
このコードではランダムな点列による多角形、三角形、六角形などの3つの多角形を描画しています。
どれも頂点の配列を求めてから、```polygon = core.Boundary(points3)```というようにBoundary関数を用いることで多角形を作成しています。

特に3つ目のようにfor文など用いて頂点を計算すれば、複雑な形状の図形も簡単に描画できたりします。
外郭を折れ線近似すれば曲線的な図形の描画もできますし、大体のパターンはこれでかけるんじゃないかなと思います。


# おわりに
デバイスレイアウト描画用のPythonモジュールであるgdsCADの使い方の基本的な使い方は触れられたんじゃないかと思います。
デバイス設計は大体GUIの設計ソフトで事足りるとは思いますが、こんな方法もあるんだなと頭の片隅に置いておくともしかしたら役に立つこともあるかもしれません。
ちなみに私はこのgdsCADのおかげで学士号を取ることができました。このモジュールのことを教えてくれた先輩に感謝。

正直プログラミング自体はあまり詳しくないので読んでて見苦しいところもあったかと思いますが、何か変なところや間違っているあればこっそり教えてもらえると嬉しいです。
もう少し強い使用例を載せるつもりでしたが結構期日に遅れてしまったのでとりあえす公開します。あとでもしかしたら追記するかも。

# 参考
[gdsCAD公式ドキュメント](http://pythonhosted.org/gdsCAD/)