このブログは、株式会社フィックスターズのエンジニアが、あらゆるテーマについて自由に書いているブログです。
 生産計画未経験者がFixstars Amplify Scheduling Engine(以下、Amplify SEと略す)を使って、半導体製造工程における仮想のテスト計画のスケジューリング最適化を試みました。
 Amplify SEは、Fixstars Amplify SDKから利用することができます。以下はSDKを用いたプログラム例です。
今回のテスト計画のスケジューリング最適化に使用したデータは、半導体製造工程の中でウエハーテストの仮想データです。
ウエハーテストは、半導体製造工程の中で製造されたウエハーを詳細に検査する重要な工程です。この工程では、製造されたウエハーに含まれる多数の半導体チップやデバイスがテストされます。しかし、テスト時間の増大により、生産プロセス全体に影響を及ぼす可能性があり、生産のボトルネックにもなり得ます。
 ウエハー種類 :10タイプ
 ウエハー枚数 :1000枚(各タイプは100枚)
 テスト装置  :9台
 テスト工程  :3工程
 テスト単位  :25枚

ウエハーはテスト単位ごとに、工程1ではテスト装置AからCの中から、工程2ではテスト装置DからFの中から、工程3ではGからIの中から1つのテスト装置が割り当てられます。
 各工程のテスト装置AからIは、それぞれ1台ずつあり、合計9台あります。
 各タイプのテストに要する時間は、各工程でのテスト装置に応じて以下のように設定しました。

ウェハー25枚あたりのタクトタイム(時間)
提供されるドキュメントには、3つの典型的なスケジューリング問題の例とその定式化についての説明が含まれています。

今回の問題では、各工程で複数のテスト装置から処理できるMachineを選択できるため、Flexible Job Shop Schedulingの例を参考にして設定を行いました。

① Fixstars Amplify SDKのimportとクラスのインスタンス化
import datetime
from amplify_sched import *
model = Model()
② カレンダー開始、ユニットタイムの設定
model.start = datetime.datetime(2023,9,1,0,0,0)  # カレンダー開始日時(2023年9月1日 0:00)
model.time_unit = datetime.timedelta(minutes=1) # 単位:1分
③ 設定データの定義
job_names = [
    "Type_01_0", "Type_01_1", "Type_01_2", "Type_01_3", 
    "Type_02_0", "Type_02_1", "Type_02_2", "Type_02_3", 
    "Type_03_0", "Type_03_1", "Type_03_2", "Type_03_3", 
    "Type_04_0", "Type_04_1", "Type_04_2", "Type_04_3", 
    "Type_05_0", "Type_05_1", "Type_05_2", "Type_05_3", 
    "Type_06_0", "Type_06_1", "Type_06_2", "Type_06_3", 
    "Type_07_0", "Type_07_1", "Type_07_2", "Type_07_3", 
    "Type_08_0", "Type_08_1", "Type_08_2", "Type_08_3", 
    "Type_09_0", "Type_09_1", "Type_09_2", "Type_09_3", 
    "Type_10_0", "Type_10_1", "Type_10_2", "Type_10_3"
    ]
    
tester_names = [
    "Tester_1", "Tester_2", "Tester_3", 
    "Tester_4", "Tester_5", "Tester_6", 
    "Tester_7", "Tester_8", "Tester_9"
    ]
type_test_times = [
    [ 50,  40,  30, 116, 106,  96,  70,  60,  50],
    [ 60,  50,  40, 126, 116, 106,  80,  70,  60],
    [ 70,  60,  50, 136, 126, 136,  90,  80,  70],    
    [ 80,  70,  60,  46,  36,  26, 100,  90,  80],
    [ 90,  80,  70,  56,  46,  36, 110, 100,  90],
    [100,  90,  80,  66,  56,  46, 120, 110, 100],
    [110, 100,  90,  76,  66,  56, 130, 120, 130],
    [120, 110, 100,  86,  76,  66,  40,  30,  20],    
    [130, 120, 110,  96,  86,  76,  50,  40,  30],
    [140, 130, 140, 106,  96,  86,  60,  50,  40]
]
④ job name, machine nameの登録
for job_name in job_names:
    model.jobs.add(job_name)
for tester_name in tester_names:
    model.machines.add(tester_name)
⑤ 処理時間の登録
def test_time(job_index, tester_index):
    return type_test_times[job_index//4][tester_index]*60
for j, job_name in enumerate(job_names):
    for p in range(3): #process index
        model.jobs[job_name].append(Task())
        for t, tester_name in enumerate(tester_names[3*p:3*p+3]):
            model.jobs[job_name][p].processing_times[tester_name] = test_time(j, 3*p+t)
⑥ tokenの設定とAmplify SEの実行
token =  "xxxxxxxxxxxxxxxxxxx"   # 発行されたtokenに書き換えてください
gantt = model.solve(token=token, timeout=10)
Amplify SEを実行する際に、スケジュールの最適化処理に対してタイムアウト時間を設定することができます。その後、実行結果に対して以下の4つの最適化状態を確認できます。
try:
    gantt = model.solve(token=token, timeout=10)
    print(gantt.status)
except RuntimeError as e:  # 例外処理:INFEASIBLEの場合
    print (e)
#状態がINFEASIBLEの場合、例外(RuntimeError)が発生するため、状態の取得には例外処理が必要
| timeout [s] | 最適化状態 | 処理開始日時 | 処理終了日時 | 経過時間 [h] | 最大経過時間比 | 
| 1 | FEASIBLE | 2023/09/01 00:00 | 2023/12/25 06:00 | 2766 | 1.000 | 
| 2 | FEASIBLE | 2023/09/01 00:00 | 2023/12/22 10:00 | 2698 | 0.975 | 
| 3 | FEASIBLE | 2023/09/01 00:00 | 2023/12/14 00:01 | 2496 | 0.902 | 
| 4 | FEASIBLE | 2023/09/01 00:00 | 2023/11/27 18:00 | 2106 | 0.761 | 
| 5 | FEASIBLE | 2023/09/01 00:00 | 2023/11/21 02:01 | 1946 | 0.704 | 
| 7 | FEASIBLE | 2023/09/01 00:00 | 2023/10/25 20:00 | 1316 | 0.476 | 
| 10 | FEASIBLE | 2023/09/01 00:00 | 2023/10/23 08:00 | 1256 | 0.454 | 
| 15 | FEASIBLE | 2023/09/01 00:00 | 2023/10/22 22:00 | 1246 | 0.450 | 
| 20 | FEASIBLE | 2023/09/01 00:00 | 2023/10/22 02:01 | 1226 | 0.443 | 
| 30 | FEASIBLE | 2023/09/01 00:00 | 2023/10/22 22:00 | 1246 | 0.450 | 
| 60 | FEASIBLE | 2023/09/01 00:00 | 2023/10/22 02:00 | 1226 | 0.443 | 
| 180 | FEASIBLE | 2023/09/01 00:00 | 2023/10/22 02:00 | 1226 | 0.443 | 
| 300 | FEASIBLE | 2023/09/01 00:00 | 2023/10/21 06:00 | 1206 | 0.436 | 
| 600 | FEASIBLE | 2023/09/01 00:00 | 2023/10/21 06:00 | 1206 | 0.436 | 

タイムアウト時間を長く設定することで、スケジュールの最適化処理にかかる時間が増加し、より最適なスケジュール結果を得ることができました。具体的な結果として、タイムアウト時間を1秒に設定した場合と比較して、十分な最適化が行われたスケジュール結果では、経過時間が 56%短縮され、著しく大きな時短効果(▲1560時間)が得られました!
Amplify SEによるスケジュール最適化の結果を、HTML形式のガントチャートで表示することができます。ガントチャートは、横軸に日時を、縦軸にはJob、Machine、Processの3つの表示オプションがあります。
最適化初期(timeout=1s)、最適化途中(timeout=5s)、最適化後期(timeout=10s)における、それぞれのガントチャートを以下に示します。
※画像をクリックするとガントチャートが表示されます。
引数 : なし(空白)
gantt.timeline().show()
引数 : machine_view=True
gantt.timeline(machine_view=True).show()
引数 : separated_by_task=True
gantt.timeline(separated_by_task=True).show()
Amplify SEによるスケジュール最適化の結果を、HTML形式のガントチャートで表示する以外にも、スケジューリングデータを取得することで様々なグラフ表示や分析データの生成が可能です。ここでは取得データからウェハー種類毎の進捗、テスターの稼働率を管理するためのグラフを作成してみました。
参考)スケジューリングデータ取得の設定例
df = pd.DataFrame(gantt.table)	# スケジューリングデータを取得
 スケジューリングデータ .table はpandasフォーマットで返されます。 グラフ作成のためのデータ処理には以下のpythonライブラリーを使用しました。


Amplify SEを活用することで、生産計画未経験者でも、Pythonプログラムの基本的な知識さえあれば、生産計画の最適化を比較的容易に行うことができました。使用した仮想のテスト計画では、最大56%の時間短縮効果が確認できました。また、最適化されたスケジューリング結果は、ガントチャートの表示や、各タイプの日別進捗率、各テスターの日別負荷率の見える化も容易でした。
半導体製造工程の生産計画のスケジューリングやその結果の可視化・分析において、Amplify SEは非常に簡単に利用でき、生産計画における人的リソースの削減にも期待できると感じました。
最後までお読みいただき、ありがとうございました。
keisuke.kimura in Livox Mid-360をROS1/ROS2で動かしてみた
Sorry for the delay in replying. I have done SLAM (FAST_LIO) with Livox MID360, but for various reasons I have not be...
Miya in ウエハースケールエンジン向けSimulated Annealingを複数タイルによる並列化で実装しました
作成されたプロファイラがとても良さそうです :) ぜひ詳細を書いていただきたいです!...
Deivaprakash in Livox Mid-360をROS1/ROS2で動かしてみた
Hey guys myself deiva from India currently i am working in this Livox MID360 and eager to knwo whether you have done the...
岩崎システム設計 岩崎 満 in Alveo U50で10G Ethernetを試してみる
仕事の都合で、検索を行い、御社サイトにたどりつきました。 内容は大変参考になりま...
Prabuddhi Wariyapperuma in Livox Mid-360をROS1/ROS2で動かしてみた
This issue was sorted....