本記事は当社が発行しているシミュレーションメールマガジンVol. 14の記事です。
シミュレーションメールマガジンの詳細・購読申込はこちら
のサポートページから
はじめに
本連載「S4コーディング入門」は、S4のプロジェクトにおけるPythonコーディングのために役立つ情報についてお伝えすることを目的としています。S4プロジェクトを思い通りにカスタマイズする一助となれば幸いです。
セミナーやチュートリアルを通してS4に触れたり、S4を購入したあとのステップとして「S4プロジェクトを思い通りにカスタマイズする」があります。そして既存のプロジェクトをいじったり、0からプロジェクトを作成してアイコンを並べたり、設定の数字を変えたりしますが、S4の機能上それだけでは望んだモデルを実現できない場合があります。またコード編集を通して生成コードを直接編集することも検討しますが、Python自体の理解が無かったり、理解できていたとしてもS4の生成コードの構造や実行時の扱いについてはS4自体への理解が必要になります。本連載では「S4プロジェクトを思い通りにカスタマイズする」を目標として、様々なS4の知識やテクニックをお伝えする予定です。
今回は最初の記事として、主にプロジェクトの生成コードとモデル上の設定の関係について説明いたします。
※S4はPython言語で書かれ、生成コードもPythonで記述されますが、本連載はPython自体の入門書ではありません。Python自体の知識は入門程度で問題ありませんが、最初の記事からS4自体の知識を主に扱い、言語については必要に応じて補うのみとなります。
生成コードについて
S4プロジェクトを実行すると、その時点での生成コードがPythonスクリプトとして作成され、コンピューター上でS4とは異なるPythonプロセスとしてシミュレーションが実行されます。S4側ではプロセス間通信を用いてPythonプロセスと情報をやりとりし、標準出力、エラーや目的関数値といった情報を受け取ってS4側に表示します。
メニュー「モデル」→「コードを表示する」で表示できる生成コードにはユーザーがプロジェクトに対して行ったほぼすべての設定が反映されています。例えば配置したアイコン1つ1つが別々のクラスとして生成コード上で表現されます。生成コードは設定を行うたびに更新されるため、アイコンを配置してから生成コードのウィンドウを開き直すといま置いたアイコンに対応するコードが挿入されていることを確認できます。
「S4プロジェクトを思い通りにカスタマイズする」という目的に照らして考えると、プロジェクト設定の操作が生成コードにどのように反映されるかを把握することが肝要となります。シミュレーション自体の実装がほぼすべてユーザーに見えることはS4の隠れた特徴であり、Python言語の知識がそのままS4シミュレーションモデル構築に活かせます。
スコープ、特に param について
メニューの「モデル」→「パラメータを編集する」で開けるパラメータウィンドウの「パラメータ設定」タブでパラメータ x を作成すると、後々 param.x で値を参照できます。ユーザーがあるアイコンのテキスト設定欄で param.x と書くと、それは生成コードに差し込まれます。
# シミュレーションパラメータの定義
class SimulationParam:
def __init__(self,
inputDir = u"input\\default",
outputDir = u"output\\default"):
self.x = next(constantValue(1.0)) #
パラメータ自体は生成コードの冒頭付近にて定義されるクラス SimulationParam のインスタンス変数として定義されます。そして生成コードの下の方にあるシミュレーター Simulator のインスタンス化箇所にてパラメータ引数 param = SimulationParam() として渡されます。
if __name__ == "__main__":
# 実行時の処理
simulator = Simulator(param = SimulationParam(),
(略)
ユーザーが参照する param はこの param です。実はユーザーが設定するアイコンのコードはすべてこの初期化( __init__ )のスコープの下に置かれます。そのため、 param でパラメータを参照できることになります。
# シミュレーターの定義
class Simulator (SimulatorBase):
def __init__(self,
param = SimulationParam(),
(略)
dataDir = u"data"):
SimulatorBase.__init__(self, param, inputDir, outputDir, showGraph, start, warm, until, odesolver, dataDir)
# 資源の定義
# 通過 の定義
class Pass (Widget):
(略)
# 動作の定義
def run(self):
self.param # このselfはrunメソッドの第一引数になる。paramが無いという AttributeError になりがち。
また、 param は Simulator.__init__ の冒頭で初期化 SimulatorBase.__init__ に渡されています。内部の処理により param は Simulator クラスのインスタンス変数になるため、実は self.param でも参照できます。しかしながら、 self は Simulator.__init__ の中で定義されるクラスが持つメソッドの第一引数として使われているため、そこで self.param と書くと self の意味が変わってしまいます。
※ 上記のコードは通過部品を置いて編集画面を開き、「コード編集」タブの冒頭で self.param と書いた場合の生成コードになります。
※ self は頻出するため Python の予約語のように思われがちですが、実は単なる慣習であり他の名前も利用できます。それでも self 以外の名前を使うことは基本的に無いため、衝突の問題について考える必要が出てまいります。
以上のように、S4のアイコンの設定画面であっても、Pythonのスコープを意識することでテキスト欄に記述しうる内容について考えやすくなります。
他アイコン参照について
モデル上に置いたアイコンやリンクの設定は、基本的にすべて Simulator.__init__ メソッド内の文としてコード生成されます。チュートリアルの「銀行の窓口」プロジェクトを例にしますと、生成部品は Generate クラスとして定義されています。 Generate は Simulator.__init__ で定義されるローカル変数になります。また、生成部品のインスタンス化の文も Simulator.__init__ のより下の方にあります。
self.wGenerate = Generate(self)
この変数名 wGenerate は生成部品のインスタンスが持つデフォルトのインスタンス変数名であり、実はユーザーが設定可能です。生成部品の編集画面の「共通設定」タブにある「オブジェクト名」がここに挿入されます。
self.wGenerate は param と同じ Simulator.__init__ 直下にあります。従って param と同様に参照が可能なのですが、 self.param でも説明した通り、大抵のクラスが持つ第一引数 self と衝突するため、素直に self.wGenerate と書いても参照できません。この場合、以下で説明する parent を経由して参照することができます。
# 通過 の定義
class Pass (Widget):
"""アイテムを無条件で通過させます。"""
def __init__(self, parent):
Widget.__init__(self, parent, u"通過", nthread = 1)
(略)
# オブジェクトの生成
self.wPass = Pass(self)
ほとんどのアイコンに対応するクラスは、 Simulator.__init__ でインスタンス化する際に self を渡します。これはアイコンに対応するクラスの __init__ 側では parent という仮引数で受け取ります。従って、 __init__ 内では parent.wGenerate という形で parent を通して生成部品を参照できます。通常、 __init__ 冒頭の Widget.__init__ 呼び出しの内部で parent はインスタンス属性にもなります。そのため __init__ 以外のメソッドでも self.parent.wGenerate で生成部品を参照できます。
※parentはPythonでクラス間の関係性を表現する際によく使われるテクニックの一つです。例えばS4が利用しているGUIライブラリ wxPython でも同様の属性が用いられます。
この手法は分岐アイコンの条件部を記述する際に利用することが多いです。例えばサンプルプロジェクトの 離散-ミニマル\制限区間.s4 を見ると、「分岐」アイコンの条件部は以下のような形になっています。
self.parent.rFacilityInner1.sizeBuffer() < self.parent.rFacilityInner1.capacity
ファシリティは部品ではなく資源ですが、 Simulator.__init__ 内で初期化されるインスタンス属性であることに変わりないため、 self.parent 経由で参照できます。
おわりに
「S4プロジェクトを思い通りにカスタマイズする」という目標を掲げ、初回は生成コードの見方や param, parent といった要素との関係について説明いたしました。今回の範囲で行えるカスタマイズはそれほど多くないかもしれませんが、実際にプロジェクトのカスタマイズを行うときは、このような個々の知識の総合としてなすべき作業が決まって参ります。そのため連載を積み重ねていくうちに、S4プロジェクトを改造する能力は少しづつ上昇していきますので、今後もお付き合いいただけますと幸いです。