本記事は当社が発行しているシミュレーションメールマガジンVol. 15の記事です。
シミュレーションメールマガジンの詳細・購読申込はこちら
のサポートページから
はじめに
前回の記事では、主にモデルの設定と生成コードの関係に注目し、S4アイコン上のコード入力でそれがどこに挿入されるか(スコープ)を意識することで他アイコンの参照といったテクニックが利用できるようになることをお伝えしました。今回は視点を変え、S4の土台となる psim とS4シミュレーションモデルの関係について見て参ります。
psimは「psim言語」と呼んでいる文書もございますが、実態は通常の Python パッケージです。歴史的経緯から、これは(現在ではあまり推奨されていない) star (*) import でインポートする習わしになっています。生成コードの冒頭に書かれている
from psim import *
が実際のインポート文です。これにより名前空間に psim パッケージで定義されているものが置かれます。何が置かれたかは、 dir() で確認できます。例えばカスタムコードで print(dir()) と打てばどのような変数が増えたかを見て取ることができます。その中には now や pause や activate といったpsimの基本的な機能も含まれます。
プロジェクトフォルダと生成コード実行について
ワークスペースのプロジェクト名の上で右クリックメニュー「エクスプローラでプロジェクトフォルダを開く」から、そのプロジェクトのデータを収めるプロジェクトフォルダを開くことができます。ユーザーが実行ボタンを押したとき、Pythonプロセスはこのフォルダ直下で実行されます。そのためこのフォルダ以下にCSVファイル table.csv を置いておくと相対パス open("table.csv") で開くことができます。

Simulator.__init__ には仮引数 inputDir = u"input\\default", outputDir = u"output\\default" があります。これらはプロジェクトフォルダに掘られた input フォルダ、 output フォルダの下に置いたファイルを参照する助けとなります。例えば input/default に table.csv を置いた場合、以下のように参照できます。
os.path.join(inputDir, "table.csv")
プロジェクトフォルダ以外に、生成コードを用いて他所でプロジェクトを実行するテクニックについてご紹介します。生成コードのウィンドウを開くと、これは入力不可のテキストボックスに生成コードが書き込まれています。これを Ctrl-A で全選択の上 Ctrl+C でコピーし、空の Python スクリプトファイル foo.py にペーストして保存すると、 foo.py は Python スクリプトとして実行可能になります。次の手順で実行してください。
- コマンドプロンプトを開き、 foo.py があるフォルダに移動(cd)します。
msipy -3 -u foo.py を入力します。
msipy はS4インストール時に同時にインストールされる MSI Python のコマンドです。このように実行した場合、実行したフォルダに input フォルダや output フォルダが作成され、結果が置かれます。 foo.py は通常の Python スクリプトであるため編集も可能であり、モデルのデバッグ等ではこの方法を利用することもあります。
psimとS4シミュレーションモデルの関係について
S4シミュレーションモデルは通常のpsimと本質的に同じ仕組みでシミュレーションを行います。psimプロセス( yield を含む def )を定義し、それを activate し、 start で実行します。
生成コードを観察すると、下の方の if __name__ == "__main__": 節において simulator をインスタンス化し、 simulator.run() で Simulator.run メソッドを呼んでいます。 Simulator.run 内で activate やpsimの start への呼び出しを確認できます。また activate は部品に対応していることがわかります。例えば activate(self.wGenerate.start)() は生成部品の start がpsimプロセスであることを示しています(アイコンを削除するとこの行も消えます)。

1個の部品は1個のpsimプロセスに対応しています。この認識はモデルに対する新しい視点をもたらしてくれます。部品以外の、例えば資源(ファシリティやストアなど)や環境(SFM地図など)もアイコンを持ちますが、対応するpsimプロセスはありません。(注意:「プロセスを持つのは部品だけ」とは言っていません。例えばエージェント系のアイコンもプロセスを持ちます。)
個々の部品について中身を見てみます。ここでは生成部品を例に取ります。生成部品の編集画面を開き、「コード編集」タブを開きます。ここには class Generate の定義が表示されます。カスタムコードにもここに書かれているコードがそのまま埋め込まれていることを確認できます。

まず Generate は Widget クラスを継承しています。他の部品でも同じ Widget を継承しています。これは、部品ごとの違いはすべて「コード編集」タブのコード上で現れることを示しています。
「コード編集」タブは上下に分かれており、上半分は編集不可能になっています。「属性設定」の設定値がここに差し込まれるためです。下半分は run メソッドの中身であり編集可能です。ここではローカルに関数 waiting, generating, sending が定義された後で
yield go(waiting)()
の呼び出しが現れます。ほとんどの部品のプロセスはこのようにいくつかの状態を表す関数定義と初期状態への go 遷移の組み合わせで表現されます。
waiting は初期生成時間まで待ってから generating へ go 遷移します。 generating では実際にアイテムを生成し、 sending へ go 遷移します。 sending では出力キューにアイテムを送信し、生成時間だけ待ってから generating へ go 遷移します。以降は generating と sending を交互に遷移していきます。
リンクについて
S4のアイコン同士をつなぐリンクにも、アイコンと同様にクラス Link が存在しており、生成コード上で
Link(self.wGenerate.output1, self.wUseFacility.input1)
などという形で表現されます。ここでは Simulator のインスタンス属性とされていませんが、 Link 作成時に引数の方で Link 自身がインスタンス属性として代入されるため、 Python のガベージコレクションでオブジェクトが消えてしまうことはありません。
_sim_before および _sim_after について
巨大な class Simulator の定義の下の方に run メソッドの定義があります。ここには _sim_before および _sim_after への呼び出しがあります。
try:
_sim_before(self)
except:
pass
(中略)
try:
_sim_after(self)
except:
pass
"_sim_before" in dir() などで dir() を確認すると、 _sim_before も _sim_after も存在しないことがわかります。そのため、そのままですと try では必ずエラーが発生し、 except 節に流れます。 _sim_before および _sim_after は、それぞれシミュレーション前と後に実行したい文を記述できるようにする仕組みです。カスタムコードで関数 def _sim_before(sim): を定義すると、シミュレーションの開始直前に _sim_before 内で記述した文を実行できます。第一引数で Simulator のインスタンスが渡されるため、これを参照して何らかの設定を行うことが多いです。典型的な例としてはモニターの登録(addMonitor)が挙げられます。
おわりに
「S4プロジェクトを思い通りにカスタマイズする」という目標を掲げ、第1回、第2回でS4プロジェクト・生成コード・psimの関係について見て参りました。すでに「使える」テクニックもいくつかご紹介しており、S4プロジェクト作成に寄与すると思われます。ただ、これまでは一般的な話題であったため、実際のS4プロジェクト作成における具体的なテクニックのご紹介はこれからとなります。S4は汎用的なシミュレーションシステムであり、ユーザーの方が「やりたいこと」のバリエーションも多様です。そのため、今後は一度一般論を離れ、具体的なサンプルプロジェクトに触れながら実際によく利用するテクニックについて順次ご紹介していく予定です。より直接「役立つ」テクニックが多くなって参りますため、今後もお付き合いいただけますと幸いです。