Python仮想環境を構築しIgorで動かす(前編)はこちら
前回の記事では、Minicondaを用いたPython仮想環境の構築と、Igor Proへの仮想環境の設定方法を確認しました。本稿では、実際にIgor ProからPythonコードを実行し、GPU情報をリアルタイムでグラフ表示するプログラムを作成・実行してみます。
今回は、Nvidia-smiから「GPU温度」と「GPU使用率」の情報を1秒ごとに取得し、Igorのグラフに逐次表示させるプログラムを作成します。なお、Igorから使用できるPythonスクリプトのディレクトリは、「メニューバー>Python>コンソールを開く」から、コンソール上で「sys.path」を実行することで確認できます。
ディレクトリを追加したい場合は、「sys.path.append(“任意のパス”)」を実行することで追加できます。
今回使用するコードは、すべて以下のディレクトリに保存しました。
C:\Users\hulinks\Documents\WaveMetrics\Igor Pro 10 User Files\Python Scripts
まずは、Python上でNvidia-smiからGPU温度とGPU使用率のデータを取得するスクリプトを作成します。基本となるコードについては次のブログからコピーしました。
https://qiita.com/tomotaka_ito/items/1da001c98b46ecf28ec7
DEFAULT_ATTRIBUTES はNvidia-smiから取得するデータキーのタプルで、本体となる関数 get_gpu_info の戻り値ディクショナリのキーにもなります。今回使うGPU使用率に対応するキー “utilization.gpu” 以外は削除し、GPU温度に対応する “temperature.gpu” を追記します。
【補足】Nvidia-smiから得られるデータのキーは、Anaconda Prompt上で「nvidia-smi –help-query-gpu」を実行することで一覧を確認することができます。
【注意】関数 get_gpu_info() の戻り値は、”DEFAULT_ATTRIBUTESで指定したキーと、それに対応する値のバイト文字列を持つDict型データ” を0番目要素に持つ List型データ です。
上記の通り、加筆修正したものが以下のコードです。
import subprocess
import json
DEFAULT_ATTRIBUTES = (
'utilization.gpu',
'temperature.gpu',
)
def get_gpu_info(nvidia_smi_path='nvidia-smi', keys=DEFAULT_ATTRIBUTES, no_units=True):
nu_opt = '' if not no_units else ',nounits'
cmd = '%s --query-gpu=%s --format=csv,noheader%s' % (nvidia_smi_path, ','.join(keys), nu_opt)
output = subprocess.check_output(cmd, shell=True)
lines = output.decode().split('\n')
lines = [ line.strip() for line in lines if line.strip() != '' ]
return [ { k: v for k, v in zip(keys, line.split(', ')) } for line in lines ]
igorproモジュールでは、ウェーブの作成やコマンドラインの実行など、Igor上で利用できる様々なクラスやメソッドが提供されています。その中でも、今回使用するものについて簡単に紹介します。
・igorpro.execute(code, ignoreErrors = False)
この関数を実行すると、codeに代入された文字列がIgorコマンドラインで実行されます。ignoreErrorsをTrueに設定するとエラーが出た場合に表示されなくなります。
・igorpro.waves.create(name, shape, value, type, folder, overwrite)
この関数を実行すると、Igor上に新しいウェーブが作成されます。各引数の説明は以下のとおりです。
・igorpro.wave.set_data(source)
この関数を実行すると、ウェーブにデータを設定します。sourceには、Igorのウェーブ、NumPy配列、Pythonの配列、リスト、タプルのいずれかを指定します。
以上で紹介したもの以外にも多数のクラスやメソッドが利用できます。一覧は「メニューバー>ヘルプ>Igorヘルプブラウザー」からヘルプブラウザーを開き、ヘルプトピック内のヘルプファイル「Python Module Reference」を開くことで確認できます。
1.2で紹介したメソッドを利用して、実際にIgor上にグラフを表示させるプログラム本体を作成します。Igor上にウェーブを作成してグラフを表示し、1.1で作成したnvidia-smi.get_gpu_info()を1秒ごとに実行し、得たデータをウェーブに上書きしていくことでグラフを描画します。
これを実行するプログラムが以下のコード「getGpuInfo.py」です。メンバとして、時間・GPU温度・GPU使用率のウェーブ名用の文字列 nameTimeWave、nameTempWave、nameUtilWave と、2つの関数 prepareIgor()、writeGraphs() が定義されていますが、writeGraphs() が今回のプログラムの本体になります。
writeGraphs() の引数 dur は継続時間[sec]を指定する引数で、デフォルト値は60です。
実行されると、最初にigorpro、nvidia-smi、timeモジュールがインポートされます。igorproはIgorを制御するためのメソッドを含むモジュール、nvidia-smiは1.1で作成したnvidia-smi.py、timeはタイマー機能を使用するためのモジュールです。また、importステートメントはasを使用して別名でインポートすることもできます。
次に、writeGraphs() は関数 prepareIgor(ig) を実行します。prepareIgor() はIgorに時間・GPU温度・GPU使用率のウェーブを作成するための関数で、戻り値はこれらのウェーブのリストです。igでigorproモジュールを受け取り、ig.wave.create() メソッドを起動します。今回はnameとoverwriteのみ指定し、他はデフォルト値を使用しました。このコマンドにより、Igor上に3つの空のウェーブ timeWave、tempWave、utilWave が作成され、これらを含むリストが writeGraphs() の waves に代入されます。
その後 writeGraphs() では ig.execute() メソッドが2回実行されます。引数には、Displayコマンドが文字列として代入されていて、Igorコマンドによって「時間vsGPU温度」「時間vsGPU使用率」の2つのグラフが生成されます。
次に定義されている ti、te、ut はそれぞれ時間・GPU温度・GPU使用率のデータを格納する空のリストです。
最後に、durで指定した秒数(正確には回数)だけforループが繰り返され、グラフが描画されます。最初の time.sleep(1) は1秒間プログラムを停止させるコマンドです。再開後、nvidia-smi.py内の get_gpu_info() が実行されるとデータがgpuInfoに代入され、その0番目要素をdataに格納します(1.1【注意】参照)。繰り返し回数をtiに、dataのうちGPU温度とGPU使用率に対応するデータをfloat型に変換して、それぞれteとutに追記し、それらをウェーブに上書きすることでウェーブが時間ごとに更新され、グラフが描画されていきます。
#作成するウェーブ名を定義
nameTimeWave = 'timeWave'
nameTempWave = 'tempWave'
nameUtilWave = 'utilWave'
def prepareIgor(ig):
#ウェーブを新規作成する
timeWave = ig.wave.create(nameTimeWave, overwrite = True)
tempWave = ig.wave.create(nameTempWave, overwrite = True)
utilWave = ig.wave.create(nameUtilWave, overwrite = True)
waves = [timeWave, tempWave, utilWave]
return waves
def writeGraphs(dur = 60): #durは継続時間
import igorpro as ig
import nvidia_smi as smi
import time
waves = prepareIgor(ig)
#グラフを新規作成
graphTe = ig.execute('Display ' + nameTempWave
+ ' vs ' + nameTimeWave
+ ' as \"Temperature\"')
graphUt = ig.execute('Display ' + nameUtilWave
+ ' vs ' + nameTimeWave
+ ' as \"Utilization\"')
#時刻、温度、使用率を格納するリストを初期化
ti = []
te = []
ut = []
for i in range(0, dur, 1):
time.sleep(1) #1秒停止
gpuInfo = smi.get_gpu_info()
data = gpuInfo[0]
ti.append(i)
te.append(float(data['temperature.gpu']))
ut.append(float(data['utilization.gpu']))
#各ウェーブを上書き
waves[0].set_data(ti)
waves[1].set_data(te)
waves[2].set_data(ut)
1.3で作成した関数 writeGraphs() をIgorのPythonコンソールで実行します。Pythonコンソールは「メニューバー>Python>コンソールを開く」から使用でき、簡単なPythonコードを入力・実行できます。

より長いコードを実行したい場合は、外部エディタで作成したモジュールをインポートし、メンバ関数を実行する方法が有利です。この方法を用いて、1.3で作成したコードを動かします。
最初にPythonコンソール上でgetGpuInfoをインポートします。

Enterを押して特に何も起こらなければ成功です。
その後、インポートしたgetGpuInfo.py内のwriteGraphs()を実行すると、グラフが描画されます(下図は実行時間を45秒間としてプログラムを実行した際の画像です)。

新バージョンIgor Pro 10で追加されたPython統合機能の使用方法を確認するため、全体を通して以下の内容を実施しました。
今回の手順を応用することで、従来のIgor標準機能の制御に留まらず、Pythonライブラリの導入や外部ソフトウェアとの連携など、多種多様なIgor Proの制御が可能になります。
このような機能に興味のある方は、ぜひIgor Pro 10をお試しください。
30日間のデモ版もありますので、興味がありましたら デモ版申請フォームよりお問い合わせください。