last updated on 09 October 2023

シミュレーターの設定

ここではシミュレーターをOACISに登録する方法について解説します。

OACISから既存のシミュレーターを実行できるようにするには、OACISの実行方式にあうようにシミュレーターを設定する必要があります。 例えば、OACISはパラメータを引数またはJSONでシミュレーターに渡しますが、既存のシミュレーターをその形式に合わせるために小さなスクリプトを用意する必要があります。 ここではその設定方法について解説していきます。


ジョブ実行の動作

まず、OACISに登録されたSimulatorがどのようにジョブが実行されるかについてより詳細に説明します。

OACISにSimulatorを登録する際には、実行プログラムそのものではなくコマンドの文字列を登録します。こうすることにより、OACISは任意の言語で書かれたプログラムを実行できるようにしていますが、実行プログラムは各ホストで事前にビルドしておく必要があります。 OACISはジョブ実行時にコマンドが埋め込まれたシェルスクリプト(以降、ジョブスクリプトと呼ぶことにします)を作成し、そのジョブスクリプトをリモートホストのジョブスケジューラ(例えばTorque)にSSH経由で投入します。 ジョブを投入する際には、各ジョブごとに一時ディレクトリ(以後、ワークディレクトリと呼びます)を作成します。 ワークディレクトリはHost登録時に”work base dir”という項目で指定したパス以下に作成されます。 ジョブスクリプトの中では、そのワークディレクトリにcdしてからコマンドを実行するようになっています。

時系列順により詳細にジョブ実行の流れを見ていきましょう。実行の流れは以下のようになっています。


OACIS-server                                  |     computational host              |   computation node
----------------------------------------------|-------------------------------------|---------------------------------------
                                           ---|-->  SSH login                       |
                                              |     create a work directory         |
                                              |     prepare _input.json             |
                                              |     create a job script             |
execute local preprocess                      |                                     |
                                              |     copy output of local preprocess |
                                              |     execute preprocess              |
                                              |     submit job script               |
                                              |                                     |   (when job script start)
                                              |                                     |   execute print-version command
                                              |                                     |   save execution logs to a file
                                              |                                     |   execution of the simulation program
                                              |                                     |   compress the work directory
                                              |                                     |
                                              |     (after the job finished)        |
                                           ---|-->  SSH login                       |
                                              |     download the compressed results |
extract the results                           |                                     |
move the output files to specified directory  |                                     |
parse logs and save them in MongoDB           |                                     |

まずOACISはリモートホストにログインし、ワークディレクトリを作成します。その後、実行パラメータが書かれた_input.jsonを配置します。(Simulator登録時にJSON形式を指定した場合)

ジョブスケジューラにジョブを投入する前に、ワークディレクトリで個別に実行する処理を定義することもできます。プリプロセスと呼んでいます。 詳細は以後説明しますが、例えばジョブの実行に必要なファイルなどをコピーしたりするのに利用します。 プリプロセスには2種類あり、OACISのあるサーバーで実行されるもの、計算ホストのログインノードで実行されるものを定義できます。(それぞれ”local pre-process”, “pre-process”と呼びます。) _input.jsonが作成された後、ジョブ投入前にこれらのプリプロセスが実行されます。 “local pre-process”についてはOACISサーバーで実行された後、出力ファイルが計算ホストのワークディレクトリに転送されます。

その後、ジョブがスケジューラに投入され、ジョブが実行待ちになります。ジョブがスケジューリングされるとジョブスクリプトが実行されることになります。

ジョブスクリプトでは、ジョブの実行を行うだけでなく、実行日時、実行ホスト、実行時間などの実行ログをファイルに書き出して保存する処理も行っています。 date, hostname, time などの各種コマンドを使っています。 これらの情報は_status.jsonというファイルに保存され、実行結果をOACISのサーバーに取り込む際に読み込まれます。

後述するようにSimulatorのバージョンを取得するコマンド(“print-version command”と呼びます)を登録することもできます。 その場合、print-version commandはシミュレーションプログラムの実行直前に実行され、その標準出力がシミュレーターのバージョンとして記録されます。 この情報は_version.txtというファイル名で保存され、ジョブがOACISのDBに取り込まれる時に読み込まれます。

さらに、ジョブスクリプトの最後でワークディレクトリを圧縮して1ファイルにしています。 こうすることにより結果のダウンロードに要する時間を削減しています。

実行するシミュレーションプログラムの要件

OACISで実行するプログラムは以下の要件を満たす必要があります。

  1. 出力ファイルが実行時のディレクトリ以下に作成される事
    • OACISは実行時にワークディレクトリを作り、その中でジョブを実行します。完了後、そのディレクトリ内のファイルすべてを出力結果として取り込みます。
  2. パラメータの入力を引数またはJSONで受け付ける事
    • 引数渡しの場合はパラメータが定義された順番に引数で渡されて、最後の引数として乱数の種が渡されます。
      • 例えば、param1=100, param2=3.0, seed(乱数の種)=12345 の場合、以下のコマンドが実行されます。
        • ~/path/to/simulator.out 100 3.0 12345
    • JSON形式の場合、実行時に次のような形式のJSONファイルを _input.json というファイル名でOACISが実行時に配置します。シミュレータはカレントディレクトリの _input.json をパースするように実装してください。
      • {"param1":100,"param2":3.0,"_seed":12345}
      • 乱数の種は _seed というキーで指定されます。
      • 実行コマンドは以下のように引数なしで実行されます。
        • ~/path/to/simulator.out
  3. 以下の名前のファイルがカレントディレクトリにあっても問題なく動作し、これらのファイルを上書きしたりしないこと
    • _input.json , _output.json , _status.json , _time.txt, _version.txt
    • これらのファイルはOACISが使用するファイル名であるため干渉しないようにする必要があります
  4. 正常終了時にリターンコード0、エラー発生時に0以外を返す事
    • リターンコードによってシミュレーションの正常終了/異常終了が判定されます。

シミュレーター設定用スクリプトのサンプル

上記のように、OACISから実行プログラムにパラメータを渡す方法は引数またはJSONである必要があります。 これらのパラメータの渡し方に準拠していない既存のシミュレーションプログラムをOACISで実行したい時には、シミュレーターの実行をラップするスクリプト(以後、ラップスクリプトと呼ぶ)をRubyやPythonなどの言語で書くのが簡単です。

OACISにはこのラップスクリプトをSimulatorとして登録し、ラップスクリプトから実際のシミュレーションプログラムを起動します。

ここではそのサンプルを示します。

例1: 引数を変更する場合

既存のシミュレーションプログラムが、オプション引数としてパラメータを渡す仕様だとしましょう。 パラメータが4つあり、それぞれオプション引数 “-l”, “-v”, “-t”, “–tmax” で渡すとします。 また乱数の種は “–seed” というオプションで渡せるとします。

例えば、

~/my_proj/my_simulator.out -l 8 -v 0.25 -t 1234 --tmax 2000 --seed 1234

という形で実行できるとします。

OACISで引数形式でパラメータを渡す場合、各パラメータが引数として順番に渡されるだけなので上記の形式には合致しません。 そこで、このプログラムをOACISのシミュレーターとして実行するために以下のようなシェルスクリプトを準備します。

#!/bin/bash

set -e
script_dir=$(cd $(dirname $BASH_SOURCE); pwd)
$script_dir/my_simulator.out -l $1 -v $2 -t $3 --tmax $4 --seed $5

このようなシェルスクリプトをmy_projに配置して実行することで、OACISから引数で与えられるパラメータを適切な形式にしてシミュレーションプログラムを実行できるようになります。

この際のポイントは

例2. パラメータを別の形式の外部ファイルで実行する場合

別の例として、既存のシミュレーションプログラムがパラメータをXML形式で受け取る場合を考えましょう。

3つのパラメータ(“length”,”velocity”,”time”)と乱数の種をXMLで指定して、そのXMLファイルを実行コマンドの引数として指定するプログラムがあったとします。例えば、

<configuration>
    <input>
        <length value="8" />
        <velocity value="25.0"/>
        <time value="2000"/>
        <seed value="1234"/>
    </input>
</configuration>

というXMLを用意して、

~/my_proj/my_simulator.xml -c configuration.xml

という形で引数としてそのXMLファイルを指定して実行するとします。

このプログラムをOACISのシミュレーターとして実行するために、例えば以下のようなpythonスクリプトwrapper.pyを準備します。 pythonから扱いやすいように、OACISにシミュレーターを登録する際にはInput typeとしてJSONを選択したとしましょう。 またwrapper.pyは実行プログラムと同じディレクトリ(~/my_proj/)に配置してあるものとします。

import os, sys, json, subprocess

# Load JSON file
fp = open( '_input.json' )
params = json.load( fp )

# Prepare input file
f = open('configuration.xml', 'w')
param_txt = """<configuration>
    <input>
        <length value="%d" />
        <velocity value="%f"/>
        <time value="%d"/>
        <seed value="%d"/>
    </input>
</configuration>
""" % (params['length'], params['velocity'], params['time'], params['_seed'])
f.write(param_txt)
f.flush()

# Execution of the simulator
simulator = os.path.abspath(os.path.dirname(__file__)) + "/my_simulator.out"
cmd = [simulator, '-c', 'configuration.xml']
sys.stderr.write("Running: %s\n" % cmd)
subprocess.check_call(cmd)
sys.stderr.write("Successfully finished\n")

スクリプトの処理の流れ

  1. パラメータの書かれたJSONファイル (_input.json) をロードする。
    • このファイルはOACISによって実行時に用意される。
  2. ファイル configuration.xml を出力する。
    • この際、ファイル出力した後に必ずflush()を呼んで、シミュレーター実行時に確実に内容が書き込まれているようにする。
  3. my_simulator.outを実行する。
    • スクリプト(wrapper.py)と同じディレクトリ上にmy_simulator.outがあるので、その絶対パスを使います。
      • 例1でもあったように my_simulator.outのパスは絶対パスで指定します。
    • シミュレーションが失敗したかどうかをリターンコードで確認して、もし0でない場合は例外を投げる。
      • OACISは wrapper.py自体のリターンコードが0かどうかで、Runが失敗したかどうかの判定を行っている。外部プロセスでエラーが起きた場合には、スクリプト自体も異常終了させるとよい。
      • pythonではsubprocess.check_callメソッドで実行すると、外部プロセスのリターンコードが0でない時に例外を送出する。

Simulatorの設定項目

OACISにSimulator登録する際に登録する項目の一覧を見ていきましょう。設定項目は以下の通りです。

フィールド 説明
Name * シミュレータの名前。Ascii文字、数字、アンダースコアのみ使用可。空白不可。他のSimulatorとの重複は不可。
Definition of Parameters * シミュレータの入力パラメータの定義。パラメータの名前、型(Integer, Float, String, Object)、デフォルト値、パラメータの説明(任意)を入力する。
Local Preprocess Script ジョブの前にローカルホストで実行されるプリプロセスを記述するスクリプト。空の場合はプリプロセスは実行されない。
Preprocess Script ジョブの前に計算ホストで実行されるプリプロセスを記述するスクリプト。空の場合はプリプロセスは実行されない。
Command * シミュレータの実行コマンド。リモートホスト上でのパスを絶対パスかホームディレクトリからの相対パスで指定する。(例. ~/path/to/simulator.out)
Pirnt version command シミュレータのversionを標準出力に出力するコマンド。(例. ~/path/to/simulator.out –version
Input type パラメータを引数形式で渡すか、JSON形式で渡すか指定する。
Support mpi シミュレータがMPIで実行されるか。チェックを入れた場合、Runの作成時にMPI並列数を指定することができる。
Support omp シミュレータがOpenMPで並列化されているか。チェックを入れた場合、Runの作成時にOMP並列数を指定することができる。
Sequential seed Runの作成時に指定されるseedをランダムな順番に与えるか、各ParameterSetごとに1から順番に与えるか指定することができる。
Description シミュレータの説明を入力する。markdownフォーマット で入力できる。
Executable_on * 実行可能Hostを指定する。ここで指定したホストがジョブ投入時に投入先ホストとして指定できる。

入力必須な項目は(*)で示されています。

Definition of Parameters の入力の際には、指定した型とデフォルト値の値が整合するように入力してください。 例えば、型がIntegerなのにデフォルト値として文字列を指定するとエラーになります。

New in v3.8.0 パラメータの型としてObject型が使えるようになりました。JSON形式で入力できます。

Local Preprocess Script または Preprocess Script を指定すると、ジョブ投入前に実行されるプリプロセスを指定することができます。 シミュレーターの入力を準備したり、ジョブ投入ノードでしか実行できない処理を指定するとよいでしょう。 詳細はプリプロセスの定義を参照してください。

Command で指定された文字列がシェルスクリプトに埋め込まれて実行されます。 OACISがジョブを実行する際には、各ジョブごとに一時的なディレクトリを作成し、その中でコマンドを実行します。 そのため、コマンドはフルパスで指定する必要があります。 様々なホストで同じように実行できるようにホームディレクトリからの相対パスで指定するとよいです。(例. ~/path/to/simulator.out

Pirnt version command を指定すると、各Runの実行時にSimulatorのバージョンも記録されます。 ここで指定したコマンドがジョブ実行用シェルスクリプトの中に埋め込まれ実行されます。その標準出力として得られた文字列がバージョンとして記録されます。 バージョンを記録すると、指定のバージョンのシミュレーターで実行されたRunを一括で削除したり、実行し直したりできます。 詳細はシミュレーターのバージョンを記録するを参照してください。

Input type はパラメータの渡し方を指定します。 引数渡しかJSONか2種類から選択できます。

MPI, OpenMPのジョブ

Simulator登録時に、 Suppot MPI, Support OMP のチェックを入れると、Runの作成時にプロセス数とスレッド数を指定するフィールドが表示されるようになります。

並列数の指定

OpenMPのジョブのスレッド数を指定すると、ジョブスクリプトの中で OMP_NUM_THREADS の環境変数がセットされます。 つまりOpenMPで並列化しているシミュレータはOMP_NUM_THREADS環境変数を参照してスレッド数を決めるように実装されている必要があります。 ( プログラム内で omp_set_num_threads() 関数で別途指定している場合は、当然ながらここで指定したスレッド数は適用されません)

MPIで並列化して実行する場合、Runの作成時に指定したプロセス数は OACIS_MPI_PROCS の環境変数にセットされます。 Simulatorの実行コマンドとして、OACIS_MPI_PROCS環境変数を参照してmpiプロセスを起動するコマンドを指定する必要があります。 以下はコマンドの例です。

mpiexec -n $OACIS_MPI_PROCS ~/path/to/simulator.out

プリプロセスの定義

シミュレータによっては実際にシミュレーションジョブを開始する前に、入力ファイルを準備したりフォーマットを調整したりするプリプロセスが必要な場合がしばしばあります。 しかしプリプロセスを計算ジョブの中で行うのが難しい場合があります。 例えば

そこで、OACISにはジョブの実行前にプリプロセスを個別に実行する仕組みを用意しています。 このプリプロセスはジョブの投入前にローカルホストまたはログインノードで実行されるため上記の問題は起きません。 プリプロセスの中には2種類あり、OACISの動いているサーバーで実行される”local pre-process” (v2.12.0より利用可能), リモートホストでジョブの投入前に実行される”pre-process”の2種類があります。

プリプロセスはジョブの投入前にworkerによって実行されます。

“local pre-process”の実行手順は以下のようになる。

  1. OACIS稼働中のサーバーにて各ジョブ用のディレクトリが作られる
  2. SimulatorがJSON入力の場合、_input.jsonを配置する
  3. Simulatorの local_pre_process_script フィールドに記載されたスクリプトをディレクトリに配置し実行権限をつける。(_lpreprocess.sh というファイル名で配置される)
  4. _lpreprocess.sh をワークディレクトリをカレントディレクトリとして実行する
    • この際Simulatorが引数形式ならば、同様の引数を与えて _lpreprocess.sh を実行する。この引数から実行パラメータを取得することができる。
    • 標準出力、標準エラー出力は _stdout.txt, _stderr.txt にそれぞれリダイレクトされる。
  5. _lpreprocess.sh のリターンコードがノンゼロの場合には、そのRunをfailedとする
  6. カレントディレクトリに作成されたファイルをリモートホスト上に転送する

“pre-process”の実行手順は

  1. SimulatorがJSON入力の場合、_input.jsonを配置する
  2. Simulatorの pre_process_script フィールドに記載されたスクリプトをワークディレクトリに配置し実行権限をつける。(_preprocess.sh というファイル名で配置される)
  3. _preprocess.sh をワークディレクトリをカレントディレクトリとして実行する
    • この際Simulatorが引数形式ならば、同様の引数を与えて _preprocess.sh を実行する。この引数から実行パラメータを取得することができる。
    • 標準出力、標準エラー出力は _stdout.txt, _stderr.txt にそれぞれリダイレクトされる。
  4. _preprocess.sh のリターンコードがノンゼロの場合には、SSHのセッションを切断しRunをfailedとする
    • failedの時には、ワークディレクトリの内容をサーバーにコピーし、リモートサーバー上のファイルは削除する

これらの”local pre-process”, “pre-process” が終わったらジョブを投入します。 ただし、 Simulatorの local_pre_process_script または pre_process_script のフィールドが空の場合には、上記の手順は実行されません。

結果をOACIS上でプロットする

通常シミュレータが出力したファイル群はそのままファイルとしてサーバー上に保存されますが、結果をデータベース内に保存することもできます。 データベース内に保存されたデータはOACISのUI上からプロットをすることができるので、結果のスカラー値(例えば時系列データの平均値や分散)を保存しておくと便利です。

結果をDB内に保存するためには、保存したいデータをJSONフォーマットでシミュレータから出力すればよいです。 _output.json という名前でカレントディレクトリ直下にJSONファイルを作成すれば、データベースへの格納時にファイルがパースされDB内に保存されます。 (既存のプログラムがJSONを出力するようになっていない場合は、ラップスクリプトの中でJSON形式の出力に変換するのがよいでしょう。)

例えば、以下のような結果を保存しておくことができます。

{
  "average": 0.25,
  "variance": 0.02,
  "hash_value": {"a": 0.7, "b": 0.4}
}

(注)ただしMongoDBの制限により、”.”を含むキーは使えません。ジョブがfailedになります。

格納された結果は各Runのページから確認することもできます。

結果の閲覧

プロットはParameterSetのページからPlotタブをクリックすると、プロットの表示画面に移動します。

プロットの種類と、横軸、縦軸や系列などを指定してください。 必要なParameterSetを集めて平均や標準誤差を計算してプロットします。

右下のマップをドラッグすることで一部分を拡大したり、ログスケールに表示を切り替えることもできます。 データ点をクリックすると対象となるParameterSetのページを表示することもできます。 画面右に表示されているURLを開くと、今表示しているプロットを再度開くことができます。

プロット

シミュレーターのバージョンを記録する

シミュレーションの実行時にどのバージョンのシミュレーターで実行したかOACISに記録をさせておくことができます。

例えば、シミュレーションを実行していくうちにシミュレーションコードにバグが見つかり、一部のシミュレーションを再実行したい場合などがあります。 RunとSimulatorのバージョンをひもづけて記録する事により、あるバージョンの実行結果を一括削除したり再実行したりすることができるようになります。 シミュレーターのソースコードを変更する可能性がある時は、バージョンを記録しておくと効率的にやり直しができるようになります。

バージョンを保存するには、Simulatorのバージョンを出力させるコマンドをOACISに登録します。 例えば

~/path/to/simulator.out --version

というコマンドでバージョン情報を出力されるシミュレーターがあるとします。 このコマンドをSimulator登録時に “Print version command” というフィールドに登録しておくと、ジョブ実行時にこのコマンドを実行し、その標準出力をバージョン情報として記録することができます。

Print version command の標準出力に出力された文字列がバージョンとして認識されるので、実行バイナリに引数を渡すだけでなく柔軟な指定が可能です。 例えば、ビルドログの一部をバージョン情報として記録したり、バージョン管理システムのコミットIDを出力するような利用方法も考えられます。

head -n 1 ~/path/to/build_log.txt
cd ~/path/to; git describe --always

Runの一括削除や一括置換はCommand Line Interface(CLI)から実行できます。 詳細はCLIのページを参照してください。

Next »