「PIONEチュートリアル」の版間の差分
(→基本4(複数ファイルの入力と複数ファイルの出力の並列処理)) |
細 (→基本) |
||
(2人の利用者による、間の368版が非表示) | |||
行5: | 行5: | ||
=== 基本1(特定のファイルを出力する) === | === 基本1(特定のファイルを出力する) === | ||
− | まず、PIONEを動かすにはルール定義書をつくる必要があります。 | + | まず、PIONEを動かすにはルール定義書をつくる必要があります。 <br> |
− | + | どんな言語でも最初に作成するHelloプログラムを作ってみましょう。 | |
次の内容のファイル'HelloWorld.pione'を作成してみましょう。 | 次の内容のファイル'HelloWorld.pione'を作成してみましょう。 | ||
行17: | 行17: | ||
End | End | ||
− | このファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic1 こちら]からダウンロードできます。 | + | このファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic1 こちら]からダウンロードできます。<br> |
− | + | この後、[[pione-client]]を実行します。 | |
− | + | ||
$ pione-client HelloWorld.pione -b helloOutput | $ pione-client HelloWorld.pione -b helloOutput | ||
行51: | 行50: | ||
<== &Anonymous:Root([],{}) | <== &Anonymous:Root([],{}) | ||
− | さっきと比べると、Main Ruleが動いていないことが分かります。 | + | さっきと比べると、Main Ruleが動いていないことが分かります。<br> |
+ | さて、改めて設定したファイルを眺めてみます。<br> | ||
+ | まず、最初に呼び出されるルール(Main)が定義されています。 | ||
− | |||
− | |||
− | |||
Rule Main | Rule Main | ||
− | Mainは、C言語などと同様に特別な意味をもつルールです。 | + | Mainは、C言語などと同様に特別な意味をもつルールです。<br> |
− | + | ||
次に、出力ファイルが定義されています。 | 次に、出力ファイルが定義されています。 | ||
output 'message.txt' | output 'message.txt' | ||
− | ここに書かれたファイルが最終的に-bで指定されたディレクトリに出力として戻ってきます。 | + | ここに書かれたファイルが最終的に-bで指定されたディレクトリに出力として戻ってきます。<br> |
Action以降が実際に起動するプログラムになります。 | Action以降が実際に起動するプログラムになります。 | ||
+ | |||
Action | Action | ||
echo "Hello PIONE world !" > message.txt | echo "Hello PIONE world !" > message.txt | ||
実際にはシェルスクリプトが動きますので、どんなものも実行が可能です。 | 実際にはシェルスクリプトが動きますので、どんなものも実行が可能です。 | ||
− | |||
− | |||
− | |||
− | |||
+ | #!/bin/csh | ||
+ | |||
+ | で始めれば、cshを使って記述することもできますし、スクリプト系の言語であれば自由に記述し、実行形式を実行出来ます。<br> | ||
最後に、RuleをEndで終了します。 | 最後に、RuleをEndで終了します。 | ||
End | End | ||
− | + | これが、一番シンプルなルールの書き方です。入力ファイルがないので、出力ファイルがなければ作成し、あれば作成しないという動作をします。<br> | |
− | + | 必要以上の動作をしないところが通常のシェルスクリプトを実行する場合と異なる点です。<br> | |
+ | <br> | ||
=== 基本2(特定のファイルを入力し、出力する(更新判定)) === | === 基本2(特定のファイルを入力し、出力する(更新判定)) === | ||
行95: | 行93: | ||
End | End | ||
− | このファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic2 こちら]からダウンロードできます。 | + | このファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic2 こちら]からダウンロードできます。<br> |
+ | 今回は、test.inという入力ファイルの中にある行頭の数字を2倍して、test.outというファイルを作り出すルールです (awkの使い方については、別途勉強してみて下さい)。<br> | ||
+ | 入力ファイルの更新判定により、ルールを実行するかどうかが変わってきます。 | ||
− | + | まず、 | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
$ mkdir MultiplyingInput | $ mkdir MultiplyingInput | ||
行140: | 行136: | ||
10 | 10 | ||
− | となり、確かに2倍の値のファイルができあがっていることが分かります。 | + | となり、確かに2倍の値のファイルができあがっていることが分かります。<br> |
もう一度、実行すると、 | もう一度、実行すると、 | ||
行149: | 行145: | ||
<== &Anonymous:Root([test.in],{}) | <== &Anonymous:Root([test.in],{}) | ||
− | + | となり、ここでも何も実行しません。<br> | |
+ | つまり、入力ファイルに比べて出力ファイルのほうが新しいので、更新判定の結果、実行しなくてよいと判断したことになります。賢いですね。 | ||
− | + | ここで、ファイルを更新してみましょう。3行目に7を付け加えます。 | |
$ echo "7" >> MultiplyingInput/test.in | $ echo "7" >> MultiplyingInput/test.in | ||
そして、実行してみましょう。 | そして、実行してみましょう。 | ||
− | $ pione-client Multiplying.pione -b MultiplyingOutput -i MultiplyingInput/ | + | |
+ | $ pione-client Multiplying.pione -b MultiplyingOutput -i MultiplyingInput/ | ||
==> &Anonymous:Root([test.in],{}) | ==> &Anonymous:Root([test.in],{}) | ||
--> Rule Application: &Anonymous:Root([test.in],{}) | --> Rule Application: &Anonymous:Root([test.in],{}) | ||
行175: | 行173: | ||
14 | 14 | ||
− | 今度は実行されました。これが更新判定によりルールが実行されるかどうかが判定されているということです。 | + | 今度は実行されました。これが更新判定によりルールが実行されるかどうかが判定されているということです。<br> |
もう一度実行しても、今度は実行されません。 | もう一度実行しても、今度は実行されません。 | ||
行194: | 行192: | ||
$ touch MultiplyingInput/test.in | $ touch MultiplyingInput/test.in | ||
− | 今回も、予想通り、動作しました。 | + | 今回も、予想通り、動作しました。<br> |
− | + | もし、動作しないようだったら、バグです。すぐに、[https://github.com/pione GITHUB/PIONE]に報告しましょう。<br> | |
− | + | この基本2が理解できれば、まず、PIONEの動きの基本が理解できたことになります。 | |
− | + | ||
− | + | ||
ところで、Actionで定義した動作の中に、見慣れない記号( {$I[1]}, {$O[1]} )が現れています。 | ところで、Actionで定義した動作の中に、見慣れない記号( {$I[1]}, {$O[1]} )が現れています。 | ||
行206: | 行202: | ||
End | End | ||
− | {$I[1]}は入力ファイル、{$O[1]}は出力ファイルを表しています。それぞれの[] | + | {$I[1]}は入力ファイル、{$O[1]}は出力ファイルを表しています。それぞれの[]の中の数字1は、input/outputで定義したそれぞれの1番目ということを意味しています。 <br> |
− | + | 次の基本3での複数ファイルの入出力では1以外が使われることになります。 <br> | |
− | + | まだ、何が便利か少し分かりづらいと思いますが、例えば、Multiplying2.pioneとして、次のように記述するとどのように動作するでしょうか。 | |
Rule Main | Rule Main | ||
行222: | 行218: | ||
$ pione-client Multiplying2.pione -b MultiplyingOutput -i MultiplyingInput/ | $ pione-client Multiplying2.pione -b MultiplyingOutput -i MultiplyingInput/ | ||
− | MultiplyingOutput/outputのディレクトリにはどんなファイルができあがったでしょうか。 | + | MultiplyingOutput/outputのディレクトリにはどんなファイルができあがったでしょうか。<br> |
− | + | そうですね。test.in.outができあがっています。何故かを考えてみましょう。<br> | |
− | そうですね。test.in.outができあがっています。何故かを考えてみましょう。 | + | <br> |
− | + | ||
=== 基本3(複数ファイルの入力と複数ファイルの出力)=== | === 基本3(複数ファイルの入力と複数ファイルの出力)=== | ||
− | + | さて、更新判定が理解できたところで、複数ファイルの入出力について考えてみましょう。<br> | |
+ | test1.in, test2.inから、それぞれ2倍した数が格納された、test1.out, test2.outが出力できる[[PIONE定義書]]を作成してみましょう。 <br> | ||
+ | ここで、名前をMultiplyingFiles.pioneとして作成してみましょう。 | ||
----- | ----- | ||
行250: | 行247: | ||
----- | ----- | ||
− | このファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic3 こちら]からダウンロードできます。 | + | このファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic3 こちら]からダウンロードできます。<br> |
+ | どうでしょうか。思ったように動きましたか。複数のファイルがあれば、複数書き並べればよいのです。 | ||
− | + | でもなんだか、同じ事をするのに、同じような行を何行も書くのは嫌だなと思いませんか。もし、2倍を5倍に変えたくなったら全ての行を書き換えなくてはなりません。<br> | |
+ | 10行ぐらいならそれでもよいけれど、100個、1000個とファイルが増えてきたらどうでしょうか。だんだん嫌になってきませんか。<br> | ||
− | |||
では、どうすれば良いでしょうか。例えば、シェルスクリプトが得意なひとは次の様なプログラムを設定することも出来ます。 | では、どうすれば良いでしょうか。例えば、シェルスクリプトが得意なひとは次の様なプログラムを設定することも出来ます。 | ||
行275: | 行273: | ||
----- | ----- | ||
− | これでも、ファイルが増えるたびにinput/outputを書き換える必要があります。 | + | これでも、ファイルが増えるたびにinput/outputを書き換える必要があります。<br> |
そこで、全てのファイルを入力し、全てのファイルを出力するように入出力を表現することも出来ます。 | そこで、全てのファイルを入力し、全てのファイルを出力するように入出力を表現することも出来ます。 | ||
行299: | 行297: | ||
output '*.out'.all | output '*.out'.all | ||
− | の部分は、*は任意の文字列を表現します。つまり、*.inは、ファイルの最後が.inで終了しているファイル全て、*. | + | の部分は、*は任意の文字列を表現します。つまり、*.inは、ファイルの最後が.inで終了しているファイル全て、*.outは、ファイルの最後が.outで終了しているファイル全てを表します。<br> |
+ | .allがついていると、全てのファイルを取り扱うことを意味しています。<br> | ||
+ | .allが付いていないときには、.eachがついていることがdefaultとして決まっています。一つひとつのファイルを別々に実行するという意味になります。<br> | ||
+ | これでずいぶんと記述しやすくなりました。 | ||
− | + | でも、これだと、*.inのどれかのファイルが更新されていると全て変更になってしまいます。試してみて下さい。なんだか無駄ですね。<br> | |
− | + | また、同時にできるはずのことをfor文を使って順次実行しているので、時間も無駄ですね。このくらいのタスクであれば、たいして時間がかかるわけではないのですが、もっと時間のかかるタスクだったらどうでしょうか。<br> | |
− | でも、これだと、*. | + | 必要なファイルの更新だけを、できれば複数のホストや一台でもマルチコアCPUを使って、同時に動かすともっと早く終了できるようになるはずです。 |
− | + | ||
− | + | ||
+ | さて、それではどのようにすればよいのでしょうか。それが次の基本4になります。だんだんPIONEらしくなってきます。<br> | ||
+ | <br> | ||
=== 基本4(複数ファイルの入力と複数ファイルの出力の並列処理)=== | === 基本4(複数ファイルの入力と複数ファイルの出力の並列処理)=== | ||
− | + | 次は、さっきと違ってallが付いていません。なにが起きるかを考えてみましょう。 | |
Rule Main | Rule Main | ||
行319: | 行320: | ||
このファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic4 こちら]からダウンロードできます。<br> | このファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic4 こちら]からダウンロードできます。<br> | ||
− | ここで、{$I[1][1]}.outは1番目のinputで拡張子を除いたファイル名を指します。(参照: [[ | + | ここで、{$I[1][1]}.outは1番目のinputで拡張子を除いたファイル名を指します。(参照: [[PIONE定義書#入力定義]])<br> |
− | + | ||
− | + | ||
下は実行した例です。 | 下は実行した例です。 | ||
行374: | 行373: | ||
<== &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) | <== &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) | ||
− | + | 4つのタスクが並列に動いている様子が分かるでしょうか。 <br> | |
+ | 確かに4つずつのタスク( ==> &Anonymous:Main([testN.in],{}))が並列に動作しているようです。<br> | ||
もう一度実行してみると下のようになります。 | もう一度実行してみると下のようになります。 | ||
行400: | 行400: | ||
<== &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) | <== &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) | ||
− | となり、test3.inだけが更新されているのが分かります。これがeachとしての振る舞いです。 | + | となり、test3.inだけが更新されているのが分かります。これがeachとしての振る舞いです。<br> |
+ | ====[[基本4の補足(eachでの速度検証)]]==== | ||
+ | eachを使った並列化処理による計算速度向上の一例を示しています。<br> | ||
+ | <br> | ||
=== 基本5(直列:フロールールの設定 )=== | === 基本5(直列:フロールールの設定 )=== | ||
− | さて、もう少し複雑なルールについて設定してみましょう。 | + | さて、もう少し複雑なルールについて設定してみましょう。<br> |
まず、ふたつのルールを組み合わせて、並列計算することを考えてみます。 | まず、ふたつのルールを組み合わせて、並列計算することを考えてみます。 | ||
Rule Main | Rule Main | ||
− | input '*.in' | + | input '*.in' |
− | output '*.out' | + | output '*.out' |
Flow | Flow | ||
rule First | rule First | ||
行431: | 行434: | ||
このファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic5 こちら]からダウンロードできます。 | このファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic5 こちら]からダウンロードできます。 | ||
− | 実行結果を以下に示します。最初に5つのタスク(First)が並列で動いているのがわかります。 | + | 実行結果を以下に示します。最初に5つのタスク(First)が並列で動いているのがわかります。<br> |
その後、終了した順に、次のルールが動いています。 | その後、終了した順に、次のルールが動いています。 | ||
行588: | 行591: | ||
<== &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) | <== &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) | ||
− | 新しくできたファイルだけが作られていることがわかります。 | + | 新しくできたファイルだけが作られていることがわかります。<br> |
それでは、ファイルの更新がかかったときはどうなるでしょうか。test2.inをtouchコマンドで更新してみました。 | それでは、ファイルの更新がかかったときはどうなるでしょうか。test2.inをtouchコマンドで更新してみました。 | ||
+ | $ touch SerialInput/test2.in | ||
$ pione-client Serial2.pione -i SerialInput/ -b Serial2Output2 | $ pione-client Serial2.pione -i SerialInput/ -b Serial2Output2 | ||
− | ==> &Anonymous:Root([test1.in,test2 | + | ==> &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) |
− | --> Rule Application: &Anonymous:Root([test1.in,test2 | + | --> Rule Application: &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) |
− | --> Distribution: &Anonymous:Root([test1.in,test2 | + | --> Distribution: &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) |
>>> &Anonymous:Main([test2.in],{}) | >>> &Anonymous:Main([test2.in],{}) | ||
>>> &Anonymous:Main([test6.in],{}) | >>> &Anonymous:Main([test6.in],{}) | ||
==> &Anonymous:Main([test2.in],{}) | ==> &Anonymous:Main([test2.in],{}) | ||
--> Rule Application: &Anonymous:Main([test2.in],{}) | --> Rule Application: &Anonymous:Main([test2.in],{}) | ||
+ | --> Distribution: &Anonymous:Main([test2.in],{}) | ||
+ | >>> &Anonymous:First([test2.in],{}) | ||
+ | ==> &Anonymous:Main([test6.in],{}) | ||
+ | --> Rule Application: &Anonymous:Main([test6.in],{}) | ||
+ | <-- Rule Application: &Anonymous:Main([test6.in],{}) | ||
+ | <== &Anonymous:Main([test6.in],{}) | ||
+ | ==> &Anonymous:First([test2.in],{}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH awk '{ print $1*2 }' test2.in > test2.route | ||
+ | SH ------------------------------------------------------------ | ||
+ | <== &Anonymous:First([test2.in],{}) | ||
+ | <-- Distribution: &Anonymous:Main([test2.in],{}) | ||
+ | --> Distribution: &Anonymous:Main([test2.in],{}) | ||
+ | >>> &Anonymous:Second([test2.route],{}) | ||
+ | ==> &Anonymous:Second([test2.route],{}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH awk '{ print $1+1 }' test2.route > test2.out | ||
+ | SH ------------------------------------------------------------ | ||
+ | <== &Anonymous:Second([test2.route],{}) | ||
+ | <-- Distribution: &Anonymous:Main([test2.in],{}) | ||
<-- Rule Application: &Anonymous:Main([test2.in],{}) | <-- Rule Application: &Anonymous:Main([test2.in],{}) | ||
+ | <-- Distribution: &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) | ||
<== &Anonymous:Main([test2.in],{}) | <== &Anonymous:Main([test2.in],{}) | ||
+ | --> Distribution: &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) | ||
+ | >>> &Anonymous:Main([test6.in],{}) | ||
==> &Anonymous:Main([test6.in],{}) | ==> &Anonymous:Main([test6.in],{}) | ||
--> Rule Application: &Anonymous:Main([test6.in],{}) | --> Rule Application: &Anonymous:Main([test6.in],{}) | ||
<-- Rule Application: &Anonymous:Main([test6.in],{}) | <-- Rule Application: &Anonymous:Main([test6.in],{}) | ||
<== &Anonymous:Main([test6.in],{}) | <== &Anonymous:Main([test6.in],{}) | ||
− | <-- Distribution: &Anonymous:Root([test1.in,test2 | + | <-- Distribution: &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) |
− | --> Distribution: &Anonymous:Root([test1.in,test2 | + | --> Distribution: &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) |
− | <-- Distribution: &Anonymous:Root([test1.in,test2 | + | <-- Distribution: &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) |
− | <-- Rule Application: &Anonymous:Root([test1.in,test2 | + | <-- Rule Application: &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) |
− | <== &Anonymous:Root([test1.in,test2 | + | <== &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) |
− | となり、新しいファイルであるtest2.in, test6. | + | となり、新しいファイルであるtest2.in, test6.inに関しては、変更の可能性があるので起動し始めます。<br> |
− | <br> | + | しかし、実際にinputファイルが更新されたtest2.inのみ処理が行われ、test2.outのみ作成されます。<br> |
+ | ====[[基本5の補足(並列処理での速度向上例)]]==== | ||
+ | 基本5のプログラムに関して速度向上の例を示しています。<br> | ||
+ | <br> | ||
=== 基本6(パラメータの定義) === | === 基本6(パラメータの定義) === | ||
− | + | 入力データは、ファイルから読み取る以外にパラメータとして使用する方法があります。<br> | |
+ | パラメータにはbasicとadvancedの2つがあります。処理上の違いは特にありませんが、前者を基本パラメータ、後者を上級者向けパラメータとして定義することで[[ユーザ]]のレベルに応じて変更可能なパラメータを区別することができます。<br> | ||
+ | 下記のように定義します。(参照:[[PIONE定義書#パラメータ定義]])<br> | ||
<pre> | <pre> | ||
行636: | 行668: | ||
param $main_val := $val * 10 | param $main_val := $val * 10 | ||
Flow | Flow | ||
− | rule Sub { | + | rule Sub {sub_val1: $main_val} |
End | End | ||
Rule Sub | Rule Sub | ||
output 'message.txt' | output 'message.txt' | ||
− | param $ | + | param $sub_val1 |
+ | param $sub_val2 := $val * 100 | ||
Action | Action | ||
echo "Basic Parameters:" > {$O[1]}; | echo "Basic Parameters:" > {$O[1]}; | ||
行653: | 行686: | ||
echo " a_val2: {$a_val2}" >> {$O[1]}; | echo " a_val2: {$a_val2}" >> {$O[1]}; | ||
echo "Parameters in Sub:" >> {$O[1]}; | echo "Parameters in Sub:" >> {$O[1]}; | ||
− | echo " | + | echo " sub_val1: {$sub_val1}" >> {$O[1]}; |
+ | echo " sub_val2: {$sub_val2}" >> {$O[1]}; | ||
End | End | ||
</pre> | </pre> | ||
− | 1つずつ定義するときはそれぞれbasic param | + | 1つずつ定義するときはそれぞれbasic param XXX、advanced param YYYのように記述します。(単にparam ZZZとしたときはbasic扱いとなります。)<br> |
− | + | まとめて定義したいときはBasic Param ~ End, Advanced Param ~ Endで囲みます。<br> | |
− | 設定したパラメータはオプション--params="{XXX:N}" | + | 定義する変数名の先頭には'''$'''を付けて記述し、''':='''にてデフォルト値を定義することができます。<br> |
− | <br> | + | またAction内で使用するときは入力ファイルや出力ファイルと同様に'''{''' '''}'''で括ります。<br> |
+ | |||
+ | 設定したパラメータはオプション--params="{XXX:N}"で値を設定して実行します。<br> | ||
+ | また、定義書内でデフォルト値を記述しておけば、--params内で設定しなかったパラメータもデフォルト値で処理されます。<br> | ||
今回はbasicのみに値を設定して実行してみます。<br> | 今回はbasicのみに値を設定して実行してみます。<br> | ||
行711: | 行748: | ||
</pre> | </pre> | ||
設定を反映した処理が実行できました。<br> | 設定を反映した処理が実行できました。<br> | ||
− | |||
− | === | + | ==== パラメータのシーケンス ==== |
− | + | ではパラメータの値を複数(シーケンス)使用した場合はどうなるのでしょうか。<br> | |
+ | valに複数の値(1|2)を入れてみます。(参照:[[PIONEの式#シーケンス]])<br> | ||
+ | |||
+ | <pre> | ||
+ | /Basic6$ pione-client ParamEcho.pione -b ParamEcho --params="{val:(1|2)}" | ||
+ | "{val:(1|2)}" | ||
+ | ==> &Anonymous:Root([],{}) | ||
+ | --> Rule Application: &Anonymous:Root([],{}) | ||
+ | --> Distribution: &Anonymous:Root([],{}) | ||
+ | >>> &Anonymous:Main([],{main_val:(<i>10)}) | ||
+ | >>> &Anonymous:Main([],{main_val:(<i>20)}) | ||
+ | ==> &Anonymous:Main([],{main_val:(<i>10)}) | ||
+ | --> Rule Application: &Anonymous:Main([],{main_val:(<i>10)}) | ||
+ | --> Distribution: &Anonymous:Main([],{main_val:(<i>10)}) | ||
+ | >>> &Anonymous:Sub([],{sub_val1:(<i>10),sub_val2:(<i>100)}) | ||
+ | >>> &Anonymous:Sub([],{sub_val1:(<i>10),sub_val2:(<i>200)}) | ||
+ | ==> &Anonymous:Main([],{main_val:(<i>20)}) | ||
+ | --> Rule Application: &Anonymous:Main([],{main_val:(<i>20)}) | ||
+ | --> Distribution: &Anonymous:Main([],{main_val:(<i>20)}) | ||
+ | >>> &Anonymous:Sub([],{sub_val1:(<i>20),sub_val2:(<i>100)}) | ||
+ | >>> &Anonymous:Sub([],{sub_val1:(<i>20),sub_val2:(<i>200)}) | ||
+ | ==> &Anonymous:Sub([],{sub_val1:(<i>10),sub_val2:(<i>100)}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH echo "Basic Parameters:" > message.txt; | ||
+ | SH echo " val: 1 2" >> message.txt; | ||
+ | SH echo " b_val: 456" >> message.txt; | ||
+ | SH echo " b_val1: 135" >> message.txt; | ||
+ | SH echo " b_val2: 246" >> message.txt; | ||
+ | SH echo "Advanced Parameters:" >> message.txt; | ||
+ | SH echo " a_val: 987" >> message.txt; | ||
+ | SH echo " a_val1: 975" >> message.txt; | ||
+ | SH echo " a_val2: 864" >> message.txt; | ||
+ | SH echo "Parameters in Sub:" >> message.txt; | ||
+ | SH echo " sub_val1: 10" >> message.txt; | ||
+ | SH echo " sub_val2: 100" >> message.txt; | ||
+ | SH ------------------------------------------------------------ | ||
+ | <== &Anonymous:Sub([],{sub_val1:(<i>10),sub_val2:(<i>100)}) | ||
+ | ==> &Anonymous:Sub([],{sub_val1:(<i>10),sub_val2:(<i>200)}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH echo "Basic Parameters:" > message.txt; | ||
+ | SH echo " val: 1 2" >> message.txt; | ||
+ | SH echo " b_val: 456" >> message.txt; | ||
+ | SH echo " b_val1: 135" >> message.txt; | ||
+ | SH echo " b_val2: 246" >> message.txt; | ||
+ | SH echo "Advanced Parameters:" >> message.txt; | ||
+ | SH echo " a_val: 987" >> message.txt; | ||
+ | SH echo " a_val1: 975" >> message.txt; | ||
+ | SH echo " a_val2: 864" >> message.txt; | ||
+ | SH echo "Parameters in Sub:" >> message.txt; | ||
+ | SH echo " sub_val1: 10" >> message.txt; | ||
+ | SH echo " sub_val2: 200" >> message.txt; | ||
+ | SH ------------------------------------------------------------ | ||
+ | <== &Anonymous:Sub([],{sub_val1:(<i>10),sub_val2:(<i>200)}) | ||
+ | <-- Distribution: &Anonymous:Main([],{main_val:(<i>10)}) | ||
+ | <-- Rule Application: &Anonymous:Main([],{main_val:(<i>10)}) | ||
+ | <== &Anonymous:Main([],{main_val:(<i>10)}) | ||
+ | ==> &Anonymous:Sub([],{sub_val1:(<i>20),sub_val2:(<i>100)}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH echo "Basic Parameters:" > message.txt; | ||
+ | SH echo " val: 1 2" >> message.txt; | ||
+ | SH echo " b_val: 456" >> message.txt; | ||
+ | SH echo " b_val1: 135" >> message.txt; | ||
+ | SH echo " b_val2: 246" >> message.txt; | ||
+ | SH echo "Advanced Parameters:" >> message.txt; | ||
+ | SH echo " a_val: 987" >> message.txt; | ||
+ | SH echo " a_val1: 975" >> message.txt; | ||
+ | SH echo " a_val2: 864" >> message.txt; | ||
+ | SH echo "Parameters in Sub:" >> message.txt; | ||
+ | SH echo " sub_val1: 20" >> message.txt; | ||
+ | SH echo " sub_val2: 100" >> message.txt; | ||
+ | SH ------------------------------------------------------------ | ||
+ | <== &Anonymous:Sub([],{sub_val1:(<i>20),sub_val2:(<i>100)}) | ||
+ | ==> &Anonymous:Sub([],{sub_val1:(<i>20),sub_val2:(<i>200)}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH echo "Basic Parameters:" > message.txt; | ||
+ | SH echo " val: 1 2" >> message.txt; | ||
+ | SH echo " b_val: 456" >> message.txt; | ||
+ | SH echo " b_val1: 135" >> message.txt; | ||
+ | SH echo " b_val2: 246" >> message.txt; | ||
+ | SH echo "Advanced Parameters:" >> message.txt; | ||
+ | SH echo " a_val: 987" >> message.txt; | ||
+ | SH echo " a_val1: 975" >> message.txt; | ||
+ | SH echo " a_val2: 864" >> message.txt; | ||
+ | SH echo "Parameters in Sub:" >> message.txt; | ||
+ | SH echo " sub_val1: 20" >> message.txt; | ||
+ | SH echo " sub_val2: 200" >> message.txt; | ||
+ | SH ------------------------------------------------------------ | ||
+ | <== &Anonymous:Sub([],{sub_val1:(<i>20),sub_val2:(<i>200)}) | ||
+ | <-- Distribution: &Anonymous:Main([],{main_val:(<i>20)}) | ||
+ | <-- Rule Application: &Anonymous:Main([],{main_val:(<i>20)}) | ||
+ | <-- Distribution: &Anonymous:Root([],{}) | ||
+ | <== &Anonymous:Main([],{main_val:(<i>20)}) | ||
+ | <-- Rule Application: &Anonymous:Root([],{}) | ||
+ | <== &Anonymous:Root([],{}) | ||
+ | /Basic6$ | ||
+ | </pre> | ||
+ | デフォルトではval=(1|2)がeachとして動作します。この場合はmain_val=(10|20)となって、それぞれsub_val1=(10|20), sub_val2=(100|200)となります。<br> | ||
+ | このとき、sub_val1, sub_val2はともにeachですので、上記のように(10, 100), (10, 200), (20, 100), (20, 200)と2*2の組み合わせでルールが動きます。<br> | ||
+ | |||
+ | では、val=(1|2).allではどうなるでしょうか。<br> | ||
+ | |||
+ | <pre> | ||
+ | /Basic6$ pione-client ParamEcho.pione -b ParamEcho --params="{val:(1|2).all}" | ||
+ | "{val:(1|2).all}" | ||
+ | ==> &Anonymous:Root([],{}) | ||
+ | --> Rule Application: &Anonymous:Root([],{}) | ||
+ | --> Distribution: &Anonymous:Root([],{}) | ||
+ | >>> &Anonymous:Main([],{main_val:(<i>10|20)}) | ||
+ | ==> &Anonymous:Main([],{main_val:(<i>10|20)}) | ||
+ | --> Rule Application: &Anonymous:Main([],{main_val:(<i>10|20)}) | ||
+ | --> Distribution: &Anonymous:Main([],{main_val:(<i>10|20)}) | ||
+ | >>> &Anonymous:Sub([],{sub_val1:(<i>10|20),sub_val2:(<i>100|200)}) | ||
+ | ==> &Anonymous:Sub([],{sub_val1:(<i>10|20),sub_val2:(<i>100|200)}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH echo "Basic Parameters:" > message.txt; | ||
+ | SH echo " val: 1 2" >> message.txt; | ||
+ | SH echo " b_val: 456" >> message.txt; | ||
+ | SH echo " b_val1: 135" >> message.txt; | ||
+ | SH echo " b_val2: 246" >> message.txt; | ||
+ | SH echo "Advanced Parameters:" >> message.txt; | ||
+ | SH echo " a_val: 987" >> message.txt; | ||
+ | SH echo " a_val1: 975" >> message.txt; | ||
+ | SH echo " a_val2: 864" >> message.txt; | ||
+ | SH echo "Parameters in Sub:" >> message.txt; | ||
+ | SH echo " sub_val1: 10 20" >> message.txt; | ||
+ | SH echo " sub_val2: 100 200" >> message.txt; | ||
+ | SH ------------------------------------------------------------ | ||
+ | <== &Anonymous:Sub([],{sub_val1:(<i>10|20),sub_val2:(<i>100|200)}) | ||
+ | <-- Distribution: &Anonymous:Main([],{main_val:(<i>10|20)}) | ||
+ | <-- Rule Application: &Anonymous:Main([],{main_val:(<i>10|20)}) | ||
+ | <== &Anonymous:Main([],{main_val:(<i>10|20)}) | ||
+ | <-- Distribution: &Anonymous:Root([],{}) | ||
+ | <-- Rule Application: &Anonymous:Root([],{}) | ||
+ | <== &Anonymous:Root([],{}) | ||
+ | /Basic6$ | ||
+ | </pre> | ||
+ | allの場合はsub_val1=(10|20), sub_val2=(100|200)として組み合わせるので、上記のように1つのルールが動くだけです。<br> | ||
+ | このようにパラメータについてもeach, allを使うことでルールの振り分けが可能です。<br> | ||
+ | <br> | ||
+ | === 基本7(条件文) === | ||
+ | ==== if文 ==== | ||
+ | 今回はif文などを用いて実行したいルールを制御してみましょう。<br> | ||
<pre> | <pre> | ||
行741: | 行918: | ||
End | End | ||
</pre> | </pre> | ||
+ | |||
このファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic7 こちら]からダウンロードできます。<br> | このファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic7 こちら]からダウンロードできます。<br> | ||
最初のパラメータvalに対して、偶数の場合と奇数の場合でそれぞれ別のルールを実行するようにしています。<br> | 最初のパラメータvalに対して、偶数の場合と奇数の場合でそれぞれ別のルールを実行するようにしています。<br> | ||
− | Rule Mainにif文がありますが、[[PIONE]]の記述ではif ~ | + | Rule Mainにif文がありますが、[[PIONE]]の記述ではif ~ endと記述します。<br> |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | <br> | + | |
では、valをデフォルト値で実行してみましょう。<br> | では、valをデフォルト値で実行してみましょう。<br> | ||
+ | |||
<pre> | <pre> | ||
/Basic7$ pione-client EvenOdd.pione -b EvenOdd | /Basic7$ pione-client EvenOdd.pione -b EvenOdd | ||
行780: | 行950: | ||
/Basic7$ | /Basic7$ | ||
</pre> | </pre> | ||
+ | |||
val=123で定義されていますので、この場合は奇数のルールが動いているのが分かります。<br> | val=123で定義されていますので、この場合は奇数のルールが動いているのが分かります。<br> | ||
− | |||
次にvalを偶数にして実行してみます。<br> | 次にvalを偶数にして実行してみます。<br> | ||
+ | |||
<pre> | <pre> | ||
/Basic7$ pione-client EvenOdd.pione -b EvenOdd --params="{val:456}" | /Basic7$ pione-client EvenOdd.pione -b EvenOdd --params="{val:456}" | ||
行810: | 行981: | ||
/Basic7$ | /Basic7$ | ||
</pre> | </pre> | ||
− | + | ||
− | <br> | + | 偶数のルールが動きます。<br> |
以上のようにして条件に合わせてルールを制御することができます。<br> | 以上のようにして条件に合わせてルールを制御することができます。<br> | ||
− | |||
− | === | + | ==== case文 ==== |
− | + | case文の場合は下記のように記述します。<br> | |
− | <br> | + | |
− | + | <pre> | |
+ | case $val % 2 | ||
+ | when 0 | ||
+ | rule Even | ||
+ | else | ||
+ | rule Odd | ||
+ | end | ||
+ | </pre> | ||
+ | |||
+ | ==== constraint ==== | ||
+ | constraintを使用して下記のように記述する方法もあります。<br> | ||
+ | |||
+ | <pre> | ||
+ | param $val := 123 | ||
+ | |||
+ | Rule Main | ||
+ | output 'message.txt' | ||
+ | Flow | ||
+ | rule Even | ||
+ | rule Odd | ||
+ | End | ||
+ | |||
+ | Rule Even | ||
+ | output 'message.txt' | ||
+ | constraint $val % 2 == 0 | ||
+ | Action | ||
+ | echo "{$val} is even." > {$O[1]}; | ||
+ | End | ||
+ | |||
+ | Rule Odd | ||
+ | output 'message.txt' | ||
+ | constraint $val % 2 == 1 | ||
+ | Action | ||
+ | echo "{$val} is odd." > {$O[1]}; | ||
+ | End | ||
+ | </pre> | ||
+ | |||
+ | ====[[デバッグのための条件文]]==== | ||
+ | 条件文を使って、デバッグの切り替えの手間が少なくなる方法を考えてみましょう。<br> | ||
<br> | <br> | ||
+ | === 基本8(インタラクティブ操作) === | ||
+ | ここまでは、コマンドを実行すると結果出力までの動作を全てプログラムに委ねる、一方通行の処理を記述してきました。<br> | ||
+ | 次は、処理の途中でユーザが操作できるようにし、それ以降の処理を制御(インタラクティブ操作)できるようにしてみましょう。<br> | ||
+ | |||
+ | 今回はウェブページから選択した入力ファイルに対して、設定した演算子(+, *)、数値で計算したファイルを出力する処理を作成します。<br> | ||
+ | 実行ファイル作成のためにパッケージを、実行のために[[PIONE Webclient]]を利用します。<br> | ||
+ | |||
+ | まず、インタラクティブ操作のための処理を[[PIONE定義書]]に記述します。<br> | ||
− | |||
<pre> | <pre> | ||
.@ PackageName :: "InteractiveCalc" | .@ PackageName :: "InteractiveCalc" | ||
行850: | 行1,064: | ||
End | End | ||
</pre> | </pre> | ||
− | + | ||
− | <br> | + | この処理では、.outファイルを出力ファイルとしてダウンロードできるようにしています。<br> |
+ | [[pione-interactive]] browserコマンドにてInteractiveページを開けるようにします。<br> | ||
+ | このとき、--publicオプションで、ページのために使用する[[html]]ファイルや[[cgi]]ファイルなどがあるディレクトリを指定します。<br> | ||
+ | そのために、事前にディレクトリ(public)を用意し、そのディレクトリの下に、etcディレクトリ内とbinディレクトリ内のすべてのファイルをそれぞれコピーしています。<br> | ||
+ | また、Interactiveページを終了した後は、作成したファイルを処理できるようにpublicディレクトリ内のデータを全てワークスペース(.)へコピーしています。<br> | ||
次に、ウェブページにて最初に呼び出されるindex.htmlを作成します。htmlファイルはetcディレクトリ内に置きます。<br> | 次に、ウェブページにて最初に呼び出されるindex.htmlを作成します。htmlファイルはetcディレクトリ内に置きます。<br> | ||
+ | |||
<pre> | <pre> | ||
<!DOCTYPE html> | <!DOCTYPE html> | ||
行885: | 行1,104: | ||
</html> | </html> | ||
</pre> | </pre> | ||
− | |||
− | そして、.cgiファイルを作成します。[[シェルスクリプト]]や[[Eosのコマンド]] | + | 最後の方にある<a href="?pione-action=finish">終了</a>は、「終了」をクリックするとワーキンングディレクトリに終了通知を行い、インタラクティブ操作を終了させる処理です。詳しくは[[インタラクションAPI]]をご覧下さい。<br> |
+ | |||
+ | そして、.cgiファイルを作成します。[[シェルスクリプト]]や[[Eosのコマンド]]などの特殊な処理はこのファイルに記述します。<br> | ||
+ | cgiファイルはbinディレクトリ内に置きます。<br> | ||
+ | |||
<pre> | <pre> | ||
#!/usr/bin/env ruby | #!/usr/bin/env ruby | ||
行941: | 行1,163: | ||
end | end | ||
</pre> | </pre> | ||
+ | |||
今回は[[Ruby]]を使って記述しています。# Output as html以下のコードで[[html]]形式に変換するようにしています。そのためにCGIクラスが必要なので、require 'cgi'とcgi = CGI.newで用意しています。このようにすると、# Main Process内のようにHTMLstrに記述する文字列を加えるだけで、自由な処理を行ってその結果を[[html]]のページを表示することができます。また、## Query to ParametersにてPOST形式のクエリを変数に変換していますが、typeがfileの場合、ファイル本体はCGIクラスメンバーParamsから受け取ります。このとき受け取るデータはファイルポインタ配列ですので、取得したい要素No.を指定する必要があります。今回、入力ファイルは1つしか格納していないので、cgi.params['inputfile'][0]となります。<br> | 今回は[[Ruby]]を使って記述しています。# Output as html以下のコードで[[html]]形式に変換するようにしています。そのためにCGIクラスが必要なので、require 'cgi'とcgi = CGI.newで用意しています。このようにすると、# Main Process内のようにHTMLstrに記述する文字列を加えるだけで、自由な処理を行ってその結果を[[html]]のページを表示することができます。また、## Query to ParametersにてPOST形式のクエリを変数に変換していますが、typeがfileの場合、ファイル本体はCGIクラスメンバーParamsから受け取ります。このとき受け取るデータはファイルポインタ配列ですので、取得したい要素No.を指定する必要があります。今回、入力ファイルは1つしか格納していないので、cgi.params['inputfile'][0]となります。<br> | ||
− | |||
− | + | コードが完成したら、[[パッケージ作成]]を行います。([[PIONEチュートリアル-package]]を参照)<br> | |
+ | なお、これらのファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic8 こちら]からダウンロードできます。<br> | ||
+ | |||
<pre> | <pre> | ||
/PIONE$ pione package build Basic8/ | /PIONE$ pione package build Basic8/ | ||
行950: | 行1,174: | ||
info: Package local:/Eos/tutorial/SampleCode/PIONE/InteractiveCalc.ppg has been built successfully. | info: Package local:/Eos/tutorial/SampleCode/PIONE/InteractiveCalc.ppg has been built successfully. | ||
</pre> | </pre> | ||
− | |||
− | + | 作成した[[パッケージ]]は[[PIONE Webclient]]にて動かすことができます。[[PIONE Webclientチュートリアル#インタラクティブ操作を含むコマンドを実行]]にて動作方法と実行結果を記載しています。<br> | |
<br> | <br> | ||
− | |||
index.htmlとFileCalc.cgiで作成したページ<br> | index.htmlとFileCalc.cgiで作成したページ<br> | ||
<table> | <table> | ||
<tr> | <tr> | ||
− | <td>[[画像:PIONE-Webclient13.png]]</td> | + | <td>[[画像:PIONE-Webclient13.png|border]]</td> |
<td>-></td> | <td>-></td> | ||
− | <td>[[画像:PIONE-Webclient14.png]]</td> | + | <td>[[画像:PIONE-Webclient14.png|border]]</td> |
</tr> | </tr> | ||
</table> | </table> | ||
+ | <br> | ||
+ | <br> | ||
+ | === 基本9([[インタラクションAPI]]の使用) === | ||
+ | [[インタラクションAPI]]を使用してワーキングディレクトリに通知を送ることで、ファイル操作や終了などを命令することができます。.htmlや.cgiに記述することにより、[[ユーザ]]からの操作を受けてこれらの命令を実行することができます。<br> | ||
+ | <br> | ||
+ | |||
+ | Annotation.pione | ||
+ | <pre> | ||
+ | .@ PackageName :: "FileOperation" | ||
+ | .@ Editor :: "Kinoshita" | ||
+ | .@ Tag :: "v0.1.0" | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | Main.pione<br> | ||
+ | |||
+ | <pre> | ||
+ | Rule Main | ||
+ | output '*.txt'.all | ||
+ | Flow | ||
+ | rule Interaction | ||
+ | End | ||
+ | </pre> | ||
+ | |||
+ | 今回は.txtファイルのみを出力するようにしています。<br> | ||
+ | |||
+ | |||
+ | Interaction.pione | ||
+ | <pre> | ||
+ | Rule Interaction | ||
+ | output '*.*'.all | ||
+ | Action | ||
+ | # build public directory for pione-interactive | ||
+ | mkdir public | ||
+ | cp etc/* public | ||
+ | cp bin/* public | ||
+ | |||
+ | # start interactive operation | ||
+ | pione-interactive browser --public public | ||
+ | |||
+ | cp public/* . | ||
+ | End | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | etc/index.html<br> | ||
+ | <pre> | ||
+ | <!DOCTYPE html> | ||
+ | <html> | ||
+ | <head> | ||
+ | <meta charset="utf-8"> | ||
+ | <title>FileOperation</title> | ||
+ | </head> | ||
+ | <body> | ||
+ | <form action="./AAA.txt" method="post" enctype="multipart/form-data"> | ||
+ | <input type="hidden" name="pione-action" value="create"/> | ||
+ | <button type="submit">作成(ファイル)</button> | ||
+ | <input type="file" name="pione-content" value=""/> | ||
+ | </form> | ||
+ | <form action="./AAA.txt" method="post" enctype="multipart/form-data"> | ||
+ | <input type="hidden" name="pione-action" value="create"/> | ||
+ | <button type="submit">作成(テキスト)</button> | ||
+ | <input type="text" name="pione-content" value=""/> | ||
+ | </form> | ||
+ | <form action="./AAA.txt" method="post" enctype="multipart/form-data"> | ||
+ | <input type="hidden" name="pione-action" value="delete"/> | ||
+ | <button type="submit">削除</button> | ||
+ | </form> | ||
+ | <form action="./list.cgi" method="post" enctype="multipart/form-data"> | ||
+ | <input type="hidden" name="pione-action" value="get"/> | ||
+ | <button type="submit">ファイル一覧の取得</button> | ||
+ | </form> | ||
+ | <br> | ||
+ | <br> | ||
+ | <a href="?pione-action=finish">終了</a> | ||
+ | </body> | ||
+ | </html> | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | それぞれのボタンよりファイル操作を行います。<br> | ||
+ | <table> | ||
+ | <tr> | ||
+ | <td>作成(ファイル):</td> | ||
+ | <td>指定したファイルをサーバ上のファイル(AAA.txt)に書き込んでアップロードします。</td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>作成(テキスト):</td> | ||
+ | <td>指定したテキストボックスの内容でサーバ上のファイル(AAA.txt)に書き込みます。</td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>削除:</td> | ||
+ | <td>サーバ上のファイル(AAA.txt)内容を削除します。</td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>ファイル一覧の取得:</td> | ||
+ | <td>サーバ上のファイル一覧を表示します。(list.cgiが動作します)</td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>終了:</td> | ||
+ | <td>操作を終了します。</td> | ||
+ | </tr> | ||
+ | </table> | ||
+ | |||
+ | |||
+ | etc/list.cgi<br> | ||
+ | <pre> | ||
+ | #!/usr/bin/env ruby | ||
+ | |||
+ | require 'cgi' | ||
+ | |||
+ | cgi = CGI.new | ||
+ | |||
+ | strHTML = "" | ||
+ | |||
+ | strHTML += <<'Block-HTML' | ||
+ | <!DOCTYPE html> | ||
+ | <html> | ||
+ | <head> | ||
+ | <meta charset="utf-8"> | ||
+ | <title>CGIページ</title> | ||
+ | </head> | ||
+ | <body> | ||
+ | <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script> | ||
+ | <div id="textDiv"></div> | ||
+ | <script type="text/javascript"> | ||
+ | var div = document.getElementById("textDiv"); | ||
+ | div.textContent = ""; | ||
+ | $.getJSON("./", {"pione-action": "list"}, function(data){ | ||
+ | $.each(data, function(file) { | ||
+ | div.textContent += this.name +"\n"; | ||
+ | }); | ||
+ | }); | ||
+ | </script> | ||
+ | <a href="Index.html">戻る</a> | ||
+ | <br> | ||
+ | <br> | ||
+ | <a href="?pione-action=finish">終了</a> | ||
+ | </body> | ||
+ | </html> | ||
+ | Block-HTML | ||
+ | |||
+ | # Output as html | ||
+ | cgi.out(type: "text/html") do | ||
+ | strHTML | ||
+ | end | ||
+ | </pre> | ||
+ | |||
+ | "./"に対して"pione-action=list"を送信したときの戻り値をgetJSONにより取得して、データ毎の"name"を出力するようにしています。<br> | ||
+ | |||
+ | これらのファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic9 こちら]からダウンロードできます。<br> | ||
+ | では、実行させてみましょう。<br> | ||
+ | |||
+ | インタラクションページを開くと下記のようになっています。<br> | ||
+ | [[画像:Outdata-PIONE-Basic9-1.png|border]]<br> | ||
+ | |||
+ | まずは選択...ボタンからテキストファイルを選択し、作成(ファイル)ボタンでサーバ内にファイルを作成(アップロード)してみましょう。<br> | ||
+ | [[画像:Outdata-PIONE-Basic9-2.png|border]]<br> | ||
+ | これによりAAA.txtが同じ内容で作成(アップロード)されます。終了した後にダウンロードすると/output/AAA.txtの内容を確認できます。<br> | ||
+ | |||
+ | また、ファイル一覧の取得ボタンよりサーバ上のファイル一覧を閲覧することができます。<br> | ||
+ | [[画像:Outdata-PIONE-Basic9-3.png|border]]<br> | ||
+ | AAA.txtが確認できます。<br> | ||
+ | |||
+ | そして削除ボタンを押すと、AAA.txtを削除します。<br> | ||
+ | [[画像:Outdata-PIONE-Basic9-4.png|border]]<br> | ||
+ | AAA.txtがなくなっているのが確認できます。<br> | ||
+ | |||
+ | 次はテキストボックスに文字列を書き込んで、作成(テキスト)ボタンを押してみましょう。<br> | ||
+ | [[画像:Outdata-PIONE-Basic9-2.png|border]]<br> | ||
+ | これによりAAA.txtがテキストの内容で作成されます。<br> | ||
+ | |||
+ | 操作を終えるときには終了をクリックして下さい。終了通知が送られてインタラクティブ処理が完了します。<br> | ||
+ | また、htmlなどを実装する上で終了時には必ず終了通知finishを送信するように注意しましょう。<br> | ||
+ | <br> | ||
+ | === 基本10(ループ文) === | ||
+ | [[PIONE定義書]]にてループ文を作成してみましょう。<br> | ||
+ | Action内はシェルスクリプトですので、for文やwhile文を使用することで容易にループを実現できます。<br> | ||
+ | しかし、Actionに記述した処理は一つのルールに書かれるため、一かたまりの処理として一つのマシンで実行されてしまいます。<br> | ||
+ | それでは、処理を分割できる[[PIONE]]の長所を活かせていません。<br> | ||
+ | ループ内の一つひとつの処理を分割し、複数のマシンで短時間に処理を行うことを考えましょう。<br> | ||
+ | |||
+ | そこで、Flowでループ文に相当する内容を作成し、ループ内の個別の処理を各々のマシンに振り分ける工夫を施します。<br> | ||
+ | |||
+ | |||
+ | 今回は、[[#基本7(制御文)]]での偶数奇数判定の処理を、最小値から最大値まで加算値を加算しながら行っていくプログラムを作成します。<br> | ||
+ | このプログラムは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic10 こちら]からダウンロードできます。<br> | ||
+ | 最大値、最小値、加算値をパラメータで設定できるようにし、ルールLoopSystemにてループ文を実現しています。<br> | ||
+ | |||
+ | <pre> | ||
+ | param $maxval := 456 | ||
+ | param $minval := 123 | ||
+ | param $dval := 37 | ||
+ | |||
+ | Rule Main | ||
+ | output '*.txt' | ||
+ | Flow | ||
+ | rule LoopSystem {val: $minval} | ||
+ | End | ||
+ | |||
+ | Rule LoopSystem | ||
+ | output '*.txt' | ||
+ | param $val | ||
+ | Flow | ||
+ | if $val <= $maxval | ||
+ | rule EvenOdd {val: $val} | ||
+ | end | ||
+ | if ($val + $dval) <= $maxval | ||
+ | rule LoopSystem {val: $val + $dval} | ||
+ | end | ||
+ | End | ||
+ | |||
+ | Rule EvenOdd | ||
+ | output '*.txt' | ||
+ | param $val | ||
+ | Flow | ||
+ | if $val % 2 == 0 | ||
+ | rule Even {val: $val} | ||
+ | else | ||
+ | rule Odd {val: $val} | ||
+ | end | ||
+ | End | ||
+ | |||
+ | Rule Even | ||
+ | output '*.txt' | ||
+ | param $val | ||
+ | Action | ||
+ | echo "{$val} is even." >> Even{$val}.txt; | ||
+ | End | ||
+ | |||
+ | Rule Odd | ||
+ | output '*.txt' | ||
+ | Action | ||
+ | echo "{$val} is odd." >> Odd{$val}.txt; | ||
+ | End | ||
+ | </pre> | ||
+ | |||
+ | まず、MainルールからLoopSystemをval=最小値で呼び出し、その中でvalが最大値になるまでは加算値を加えながら再帰的にLoopSystemを呼び出しています。<br> | ||
+ | 出力ファイル名を偶数のときはEven{$val}.txt、奇数のときはOdd{$val}.txtとしています。<br><br> | ||
+ | |||
+ | では、実行してみます。<br> | ||
+ | |||
+ | <pre> | ||
+ | /Basic10$ pione-client EvenOddLoop.pione | ||
+ | ==> &Anonymous:Root([],{}) | ||
+ | --> Rule Application: &Anonymous:Root([],{}) | ||
+ | --> Distribution: &Anonymous:Root([],{}) | ||
+ | >>> &Anonymous:Main([],{}) | ||
+ | ==> &Anonymous:Main([],{}) | ||
+ | --> Rule Application: &Anonymous:Main([],{}) | ||
+ | --> Distribution: &Anonymous:Main([],{}) | ||
+ | >>> &Anonymous:LoopSystem([],{val:(<i>123)}) | ||
+ | ==> &Anonymous:LoopSystem([],{val:(<i>123)}) | ||
+ | --> Rule Application: &Anonymous:LoopSystem([],{val:(<i>123)}) | ||
+ | --> Distribution: &Anonymous:LoopSystem([],{val:(<i>123)}) | ||
+ | >>> &Anonymous:EvenOdd([],{val:(<i>123)}) | ||
+ | >>> &Anonymous:LoopSystem([],{val:(<i>160)}) | ||
+ | ==> &Anonymous:EvenOdd([],{val:(<i>123)}) | ||
+ | --> Rule Application: &Anonymous:EvenOdd([],{val:(<i>123)}) | ||
+ | --> Distribution: &Anonymous:EvenOdd([],{val:(<i>123)}) | ||
+ | >>> &Anonymous:Odd([],{val:(<i>123)}) | ||
+ | ==> &Anonymous:LoopSystem([],{val:(<i>160)}) | ||
+ | --> Rule Application: &Anonymous:LoopSystem([],{val:(<i>160)}) | ||
+ | --> Distribution: &Anonymous:LoopSystem([],{val:(<i>160)}) | ||
+ | >>> &Anonymous:EvenOdd([],{val:(<i>160)}) | ||
+ | >>> &Anonymous:LoopSystem([],{val:(<i>197)}) | ||
+ | ==> &Anonymous:Odd([],{val:(<i>123)}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH echo "123 is odd." >> Odd123.txt; | ||
+ | SH ------------------------------------------------------------ | ||
+ | <== &Anonymous:Odd([],{val:(<i>123)}) | ||
+ | |||
+ | |||
+ | -中略- | ||
+ | |||
+ | |||
+ | ==> &Anonymous:Even([],{val:(<i>456)}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH echo "456 is even." >> Even456.txt; | ||
+ | SH ------------------------------------------------------------ | ||
+ | <== &Anonymous:Even([],{val:(<i>456)}) | ||
+ | <-- Distribution: &Anonymous:EvenOdd([],{val:(<i>456)}) | ||
+ | <-- Rule Application: &Anonymous:EvenOdd([],{val:(<i>456)}) | ||
+ | <== &Anonymous:EvenOdd([],{val:(<i>456)}) | ||
+ | |||
+ | |||
+ | -中略- | ||
+ | |||
+ | |||
+ | <-- Distribution: &Anonymous:LoopSystem([],{val:(<i>160)}) | ||
+ | <-- Rule Application: &Anonymous:LoopSystem([],{val:(<i>160)}) | ||
+ | <== &Anonymous:LoopSystem([],{val:(<i>160)}) | ||
+ | <-- Distribution: &Anonymous:LoopSystem([],{val:(<i>123)}) | ||
+ | <-- Rule Application: &Anonymous:LoopSystem([],{val:(<i>123)}) | ||
+ | <== &Anonymous:LoopSystem([],{val:(<i>123)}) | ||
+ | <-- Distribution: &Anonymous:Main([],{}) | ||
+ | <-- Rule Application: &Anonymous:Main([],{}) | ||
+ | <-- Distribution: &Anonymous:Root([],{}) | ||
+ | <-- Rule Application: &Anonymous:Root([],{}) | ||
+ | <== &Anonymous:Root([],{}) | ||
+ | <== &Anonymous:Main([],{}) | ||
+ | </pre> | ||
+ | |||
+ | 今回はパラメータをデフォルト値で実行しましたので、123から456まで37刻みで実行されます。lsコマンドで出力ファイルを確認します。<br> | ||
+ | |||
+ | <pre> | ||
+ | /Basic10$ ls process/output/ | ||
+ | Even160.txt Even308.txt Even456.txt Odd197.txt Odd345.txt | ||
+ | Even234.txt Even382.txt Odd123.txt Odd271.txt Odd419.txt | ||
+ | </pre> | ||
+ | |||
+ | それぞれの値について偶数奇数の処理が実行されていることが確認できました。<br> | ||
+ | <br> | ||
+ | === 基本11(ループ文2) === | ||
+ | [[#基本10(ループ文)]]でループの作成方法を示しました。<br> | ||
+ | しかし、[[#基本10(ループ文)]]の書き方では逐次処理となってしまうので、ループ数に比例して処理時間が掛かってしまいます。<br> | ||
+ | この問題を解決する方法を考えましょう。<br> | ||
+ | 以下に示すのは、val=0がmax=64になるまで1を足し続け、{$val}.txtを作成する、ループを使った処理の例です。<br> | ||
+ | 以下に示していくファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic11 こちら]からダウンロードできます。<br> | ||
+ | |||
+ | <pre> | ||
+ | param $max := 64 | ||
+ | |||
+ | Rule Main | ||
+ | output '*.txt' | ||
+ | Flow | ||
+ | rule LoopSystem {val: 0} | ||
+ | End | ||
+ | |||
+ | Rule LoopSystem | ||
+ | output '*.txt' | ||
+ | param $val | ||
+ | Flow | ||
+ | rule Test {val: $val} | ||
+ | if $val + 1 <= $max | ||
+ | rule LoopSystem {val: $val + 1} | ||
+ | end | ||
+ | End | ||
+ | |||
+ | Rule Test | ||
+ | output '{$val}.txt' | ||
+ | param $val | ||
+ | Action | ||
+ | touch {$O[1]} | ||
+ | End | ||
+ | </pre> | ||
+ | |||
+ | このままでは、[[#基本10(ループ文)]]同様、ループ部分は、逐次処理になっています。<br> | ||
+ | そこで、ループの箇所を下記のような分割した形に書き換え、並列処理を試みます。<br> | ||
+ | |||
+ | <pre> | ||
+ | Rule LoopSystem | ||
+ | output '*.txt' | ||
+ | param $val | ||
+ | Flow | ||
+ | rule Test {val: $val} | ||
+ | if ($val * 2) + 1 <= $max | ||
+ | rule LoopSystem {val: ($val * 2) + 1} | ||
+ | end | ||
+ | if ($val * 2) + 2 <= $max | ||
+ | rule LoopSystem {val: ($val * 2) + 2} | ||
+ | end | ||
+ | End | ||
+ | </pre> | ||
+ | |||
+ | このようにすることでループに掛かる時間が改善されます。<br> | ||
+ | あるいはconstraintを使用して下記のように記述する方法もあります。<br> | ||
+ | |||
+ | <pre> | ||
+ | Rule LoopSystem | ||
+ | output '*.txt' | ||
+ | param $val | ||
+ | constraint $val <= $max | ||
+ | Flow | ||
+ | rule Test {val: $val} | ||
+ | rule LoopSystem {val: ($val * 2) + 1} | ||
+ | rule LoopSystem {val: ($val * 2) + 2} | ||
+ | End | ||
+ | </pre> | ||
+ | |||
+ | また、valの値を加算している箇所ですが、<br> | ||
+ | |||
+ | <pre> | ||
+ | rule LoopSystem {val: ($val * 2) + 1} | ||
+ | rule LoopSystem {val: ($val * 2) + 2} | ||
+ | </pre> | ||
+ | |||
+ | シーケンスを利用して下記のように書くこともできます。(参照:[[PIONEの式#シーケンス]])<br> | ||
+ | |||
+ | <pre> | ||
+ | rule LoopSystem {val: ($val * 2) + (1|2)} | ||
+ | </pre> | ||
+ | |||
+ | 問題:これを踏まえて[[#基本10(ループ文)]]のループを改善してみましょう。<br> | ||
+ | |||
+ | 解答例1<br> | ||
+ | <pre> | ||
+ | Rule LoopSystem | ||
+ | output '*.txt'.all | ||
+ | param $val | ||
+ | Flow | ||
+ | if $val <= $maxval | ||
+ | rule EvenOdd {val: $val} | ||
+ | end | ||
+ | if (($val * 2) - $minval) + $dval <= $maxval | ||
+ | rule LoopSystem {val: (($val * 2) - $minval) + $dval} | ||
+ | end | ||
+ | if (($val * 2) - $minval) + ($dval * 2) <= $maxval | ||
+ | rule LoopSystem {val: (($val * 2) - $minval) + ($dval * 2)} | ||
+ | end | ||
+ | End | ||
+ | </pre> | ||
+ | |||
+ | Flow内の条件を分割しています。条件式が多少複雑になっていますので、演算の順番に注意しましょう。<br> | ||
+ | また、現在のバージョンでの四則演算は二項演算のみの対応となっていますので、三項以上の場合は必ず括弧で括るようにして下さい(参照:[[PIONEの式#四則演算における注意事項]])。<br> | ||
+ | |||
+ | 解答例2:constraintとシーケンスを利用した場合<br> | ||
+ | <pre> | ||
+ | Rule LoopSystem | ||
+ | output '*.txt'.all | ||
+ | param $val | ||
+ | constraint $val <= $maxval | ||
+ | Flow | ||
+ | rule EvenOdd {val: $val} | ||
+ | rule LoopSystem {val: (($val * 2) - $minval) + ($dval * (1|2))} | ||
+ | End | ||
+ | </pre> | ||
+ | |||
+ | これにより[[#基本10(ループ文)]]よりも呼び出されるルールの階層は浅くなります。<br> | ||
+ | しかし、いずれも入れ子で呼び出されるのでルールが多くなるほど処理が重くなるでしょう。<br> | ||
+ | そこで1つのルールから一気にループ数だけのルールを呼び出す方法を考えます。この記述方法については[[#基本15(ループ文3)]]をご覧下さい。<br> | ||
+ | <br> | ||
+ | === 基本12(チケット) === | ||
+ | [[PIONE]]の目的の一つは、できる限り並列処理を行うことですので、必ずしも記述の順番通りにルールが実行される訳ではありません。<br> | ||
+ | ルールに順番を持たせたいときにはチケットという機能を利用します。但し、使い過ぎると待ち時間が多くなり並列計算できる[[PIONE]]の長所が活かせなくなってしまいますので注意が必要です。<br> | ||
+ | |||
+ | 下記の内容をご覧下さい。(CreateList-instant.pione)<br> | ||
+ | このファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic12 こちら]からダウンロードできます。<br> | ||
+ | |||
+ | <pre> | ||
+ | Rule Main | ||
+ | input '*.data'.all | ||
+ | output 'list.txt' | ||
+ | Flow | ||
+ | rule Touch0 | ||
+ | rule CreateList | ||
+ | End | ||
+ | |||
+ | Rule Touch0 | ||
+ | output '0.data' | ||
+ | Action | ||
+ | sleep 1 | ||
+ | touch {$O[1]} | ||
+ | End | ||
+ | |||
+ | Rule CreateList | ||
+ | input '*.data'.all | ||
+ | output 'list.txt' | ||
+ | Action | ||
+ | ls *.data > {$O[1]} | ||
+ | End | ||
+ | </pre> | ||
+ | |||
+ | これはCreateListで.dataファイルの一覧をlist.txtに記述する処理ですが、途中のTouch0でも0.dataファイルを作成しています。<br> | ||
+ | この0.dataが一覧に含まれるかを検証してみましょう。<br> | ||
+ | |||
+ | 入力ファイル用のディレクトリ(input)内 | ||
+ | <pre> | ||
+ | /Basic12$ ls input/ | ||
+ | 1.data 2.data | ||
+ | </pre> | ||
+ | |||
+ | まずはMainルールを上記のようにそのまま記述している場合で実行してみます。<br> | ||
+ | <pre> | ||
+ | /Basic12$ pione-client CreateList-instant.pione -i input/ | ||
+ | ==> &Anonymous:Root([.DS_Store,1.data,2.data],{}) | ||
+ | --> Rule Application: &Anonymous:Root([.DS_Store,1.data,2.data],{}) | ||
+ | --> Distribution: &Anonymous:Root([.DS_Store,1.data,2.data],{}) | ||
+ | >>> &Anonymous:Main([1.data,2.data],{}) | ||
+ | ==> &Anonymous:Main([1.data,2.data],{}) | ||
+ | --> Rule Application: &Anonymous:Main([1.data,2.data],{}) | ||
+ | --> Distribution: &Anonymous:Main([1.data,2.data],{}) | ||
+ | >>> &Anonymous:Touch0([],{}) | ||
+ | >>> &Anonymous:CreateList([1.data,2.data],{}) | ||
+ | ==> &Anonymous:Touch0([],{}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH sleep 1 | ||
+ | SH touch 0.data | ||
+ | SH ------------------------------------------------------------ | ||
+ | <== &Anonymous:Touch0([],{}) | ||
+ | ==> &Anonymous:CreateList([1.data,2.data],{}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH ls *.data > list.txt | ||
+ | SH ------------------------------------------------------------ | ||
+ | <== &Anonymous:CreateList([1.data,2.data],{}) | ||
+ | <-- Distribution: &Anonymous:Main([1.data,2.data],{}) | ||
+ | <-- Rule Application: &Anonymous:Main([1.data,2.data],{}) | ||
+ | <-- Distribution: &Anonymous:Root([.DS_Store,1.data,2.data],{}) | ||
+ | <== &Anonymous:Main([1.data,2.data],{}) | ||
+ | <-- Rule Application: &Anonymous:Root([.DS_Store,1.data,2.data],{}) | ||
+ | <== &Anonymous:Root([.DS_Store,1.data,2.data],{}) | ||
+ | </pre> | ||
+ | |||
+ | では出力ファイルlist.txtを確認してみます。<br> | ||
+ | <pre> | ||
+ | /Basic12$ cat process/output/list.txt | ||
+ | 1.data | ||
+ | 2.data | ||
+ | </pre> | ||
+ | 0.dataが含まれませんでした。<br> | ||
+ | これはTouch0とCreateListが同時に動き、Touch0が完了する前にCreateListが実行されているため、作成された0.dataがCreateListの入力ファイルとして登録されなかったからです。<br> | ||
+ | |||
+ | では、チケットの機能を使ってMainルールを下記のようにしてみます。<br> | ||
+ | <pre> | ||
+ | Rule Main | ||
+ | input '*.data'.all | ||
+ | output 'list.txt' | ||
+ | Flow | ||
+ | rule Touch0 ==> <T> | ||
+ | rule <T> ==> CreateList | ||
+ | End | ||
+ | </pre> | ||
+ | |||
+ | 上記のようにチケット名は<>で囲んで、ルールとチケット間を==>で繋いで使用します。<br> | ||
+ | |||
+ | チケット名を省略したい場合は下記のように記述します。<br> | ||
+ | <pre> | ||
+ | rule Touch0 >>> CreateList | ||
+ | </pre> | ||
+ | |||
+ | 下記のように連続させることも可能です。<br> | ||
+ | <pre> | ||
+ | rule First >>> Second >>> Third | ||
+ | </pre> | ||
+ | |||
+ | また複数チケットの発行もできます。<br> | ||
+ | <pre> | ||
+ | rule Touch0 ==> (<T1>|<T2>) | ||
+ | rule <T1> ==> CreateList | ||
+ | rule <T2> ==> CreateList2 | ||
+ | </pre> | ||
+ | |||
+ | 逆に複数チケットからのルール実行も可能です。<br> | ||
+ | <pre> | ||
+ | rule Touch0 ==> <T0> | ||
+ | rule Touch1 ==> <T1> | ||
+ | rule (<T0>|<T1>) ==> CreateList | ||
+ | </pre> | ||
+ | 上記の場合はチケットT0かつT1が発行されてからCreateListが実行されます。<br> | ||
+ | |||
+ | では、実行してみます。<br> | ||
+ | <pre> | ||
+ | /Basic12$ pione-client CreateList-ticket.pione -i input/ | ||
+ | ==> &Anonymous:Root([.DS_Store,1.data,2.data],{}) | ||
+ | --> Rule Application: &Anonymous:Root([.DS_Store,1.data,2.data],{}) | ||
+ | --> Distribution: &Anonymous:Root([.DS_Store,1.data,2.data],{}) | ||
+ | >>> &Anonymous:Main([1.data,2.data],{}) | ||
+ | ==> &Anonymous:Main([1.data,2.data],{}) | ||
+ | --> Rule Application: &Anonymous:Main([1.data,2.data],{}) | ||
+ | --> Distribution: &Anonymous:Main([1.data,2.data],{}) | ||
+ | >>> &Anonymous:Touch0([],{}) | ||
+ | ==> &Anonymous:Touch0([],{}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH sleep 1 | ||
+ | SH touch 0.data | ||
+ | SH ------------------------------------------------------------ | ||
+ | <== &Anonymous:Touch0([],{}) | ||
+ | <-- Distribution: &Anonymous:Main([1.data,2.data],{}) | ||
+ | --> Distribution: &Anonymous:Main([1.data,2.data],{}) | ||
+ | >>> &Anonymous:CreateList([1.data,2.data,0.data],{}) | ||
+ | ==> &Anonymous:CreateList([1.data,2.data,0.data],{}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH ls *.data > list.txt | ||
+ | SH ------------------------------------------------------------ | ||
+ | <== &Anonymous:CreateList([1.data,2.data,0.data],{}) | ||
+ | <-- Distribution: &Anonymous:Main([1.data,2.data],{}) | ||
+ | <-- Rule Application: &Anonymous:Main([1.data,2.data],{}) | ||
+ | <-- Distribution: &Anonymous:Root([.DS_Store,1.data,2.data],{}) | ||
+ | <== &Anonymous:Main([1.data,2.data],{}) | ||
+ | <-- Rule Application: &Anonymous:Root([.DS_Store,1.data,2.data],{}) | ||
+ | <== &Anonymous:Root([.DS_Store,1.data,2.data],{}) | ||
+ | /Basic12$ cat process/output/list.txt | ||
+ | 0.data | ||
+ | 1.data | ||
+ | 2.data | ||
+ | </pre> | ||
+ | 今度は0.dataが含まれるようになりました。<br> | ||
+ | <br> | ||
+ | === 基本13(orの使用例) === | ||
+ | inputやoutputで宣言するときにorや|で繋ぐと、指定した複数種類のファイルからいずれかを入力や出力に使用することができます。<br> | ||
+ | 以下で使用するファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic13 こちら]からダウンロードできます。<br> | ||
+ | |||
+ | ==== 入力ファイルでの使用例 ==== | ||
+ | <pre> | ||
+ | Rule Main | ||
+ | input '1.txt' or '2.txt' or '3.txt' | ||
+ | output 'out.txt' | ||
+ | Action | ||
+ | cp {$I[1]} {$O[1]} | ||
+ | End | ||
+ | </pre> | ||
+ | 上記では入力ファイルに1.txt, 2.txt, 3.txtのいずれかがあればout.txtにコピーする処理となっています。<br> | ||
+ | |||
+ | 例えば2.txtだけあった場合でも実行されます。<br> | ||
+ | <pre> | ||
+ | /Basic13$ pione-client test.pione -i ./input/ | ||
+ | ==> &Anonymous:Root([.DS_Store,2.txt],{}) | ||
+ | --> Rule Application: &Anonymous:Root([.DS_Store,2.txt],{}) | ||
+ | --> Distribution: &Anonymous:Root([.DS_Store,2.txt],{}) | ||
+ | >>> &Anonymous:Main([2.txt],{}) | ||
+ | ==> &Anonymous:Main([2.txt],{}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH cp 2.txt out.txt | ||
+ | SH ------------------------------------------------------------ | ||
+ | <== &Anonymous:Main([2.txt],{}) | ||
+ | <-- Distribution: &Anonymous:Root([.DS_Store,2.txt],{}) | ||
+ | <-- Rule Application: &Anonymous:Root([.DS_Store,2.txt],{}) | ||
+ | <== &Anonymous:Root([.DS_Store,2.txt],{}) | ||
+ | </pre> | ||
+ | 上記の使用例は入力ファイルが1つだけあった場合のみに対応しています。<br> | ||
+ | 複数のファイルがあった場合、複数ファイルで上書きコピーされてしまいます。<br> | ||
+ | |||
+ | ==== 出力ファイルでの使用例 ==== | ||
+ | <pre> | ||
+ | param $Mode := 1 | ||
+ | |||
+ | Rule Main | ||
+ | output 'out1.txt' or 'out2.txt' or 'err.txt' | ||
+ | Action | ||
+ | if [ {$Mode} -eq 1 ] ; then | ||
+ | echo "Mode: {$Mode}" > {$O[1].nth(1)} | ||
+ | elif [ {$Mode} -eq 2 ] ;then | ||
+ | echo "Mode: {$Mode}" > {$O[1].nth(2)} | ||
+ | else | ||
+ | echo "Error: Mode {$Mode} is not Supported!" > {$O[1].nth(3)} | ||
+ | fi | ||
+ | End | ||
+ | </pre> | ||
+ | これはパラメータModeが1のときはout1.txtを作成し、2のときはout2.txtを作成し、それ以外についてはエラーを残す処理となっています。<br> | ||
+ | out1.txt, out2.txt, err.txtのいずれかが出力されることで処理が完了しますので、状況に応じて作成するファイルを変えたいときに有効です。<br> | ||
+ | .nth()は引数で指定した要素を取り出すシーケンスのメソッドです。(参照:[[PIONEの式#シーケンス]])<br> | ||
+ | |||
+ | デフォルト(Mode=1)で実行してみます。<br> | ||
+ | <pre> | ||
+ | /Basic13$ pione-client OutputOr.pione | ||
+ | ==> &Anonymous:Root([],{}) | ||
+ | --> Rule Application: &Anonymous:Root([],{}) | ||
+ | --> Distribution: &Anonymous:Root([],{}) | ||
+ | >>> &Anonymous:Main([],{}) | ||
+ | ==> &Anonymous:Main([],{}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH if [ 1 -eq 1 ] ; then | ||
+ | SH echo "Mode: 1" > out1.txt | ||
+ | SH elif [ 1 -eq 2 ] ;then | ||
+ | SH echo "Mode: 1" > out2.txt | ||
+ | SH else | ||
+ | SH echo "Error: Mode 1 is not Supported!" > err.txt | ||
+ | SH fi | ||
+ | SH ------------------------------------------------------------ | ||
+ | <-- Distribution: &Anonymous:Root([],{}) | ||
+ | <-- Rule Application: &Anonymous:Root([],{}) | ||
+ | <== &Anonymous:Root([],{}) | ||
+ | <== &Anonymous:Main([],{}) | ||
+ | /Basic13$ cat process/output/out1.txt | ||
+ | Mode: 1 | ||
+ | </pre> | ||
+ | out1.txtが作成されて完了します。<br> | ||
+ | |||
+ | Mode=0で実行してみます。<br> | ||
+ | <pre> | ||
+ | /Basic13$ pione-client OutputOr.pione --params={Mode:0} | ||
+ | "{Mode:0}" | ||
+ | ==> &Anonymous:Root([],{}) | ||
+ | --> Rule Application: &Anonymous:Root([],{}) | ||
+ | --> Distribution: &Anonymous:Root([],{}) | ||
+ | >>> &Anonymous:Main([],{}) | ||
+ | ==> &Anonymous:Main([],{}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH if [ 0 -eq 1 ] ; then | ||
+ | SH echo "Mode: 0" > out1.txt | ||
+ | SH elif [ 0 -eq 2 ] ;then | ||
+ | SH echo "Mode: 0" > out2.txt | ||
+ | SH else | ||
+ | SH echo "Error: Mode 0 is not Supported!" > err.txt | ||
+ | SH fi | ||
+ | SH ------------------------------------------------------------ | ||
+ | <-- Distribution: &Anonymous:Root([],{}) | ||
+ | <== &Anonymous:Main([],{}) | ||
+ | <-- Rule Application: &Anonymous:Root([],{}) | ||
+ | <== &Anonymous:Root([],{}) | ||
+ | /Basic13$ cat process/output/err.txt | ||
+ | Error: Mode 0 is not Supported! | ||
+ | </pre> | ||
+ | err.txtが作成されて完了します。<br> | ||
+ | <br> | ||
+ | === 基本14(例外) === | ||
+ | [[PIONE]]でも入出力ファイルにワイルドカード*を使用して全ての該当ファイルを対象とできますが、exceptを使用することにより対象外とするファイルを指定できます。メソッドexcept, exceptionsについては[[PIONEの式#データ表現型]]を参照して下さい。<br> | ||
+ | |||
+ | <pre> | ||
+ | Rule Main | ||
+ | input 'AllData.txt' | ||
+ | output '*.txt'.all.except($I[1]) | ||
+ | Flow | ||
+ | rule GetLine {filename:"Data1", line:1} | ||
+ | rule GetLine {filename:"Data2", line:2} | ||
+ | rule GetLine {filename:"Data3", line:3} | ||
+ | End | ||
+ | |||
+ | Rule GetLine | ||
+ | input 'AllData.txt' | ||
+ | output '{$filename}.txt' | ||
+ | param $filename | ||
+ | param $line | ||
+ | Action | ||
+ | head -{$line} {$I[1]} | tail -1 > {$O[1]} | ||
+ | End | ||
+ | </pre> | ||
+ | |||
+ | 上記はAllData.txtから1, 2, 3行目を指定した.txtファイルに出力する処理です。出力ファイルは全ての.txtとしていますが、exceptによってAllData.txtを対象外としています。対象外としていない場合はAllData.txtが出力ファイルの条件を満たしていますので、発火しません。<br> | ||
+ | このファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic14 こちら]からダウンロードできます。<br> | ||
+ | |||
+ | AllData.txtを下記の内容として実行してみます。<br> | ||
+ | <pre> | ||
+ | ABC | ||
+ | DEF | ||
+ | GHI | ||
+ | |||
+ | </pre> | ||
+ | |||
+ | 実行結果<br> | ||
+ | <pre> | ||
+ | /Basic14$ pione-client DivideText.pione -i DivideTextInput/ | ||
+ | ==> &Anonymous:Root([.DS_Store,AllData.txt],{}) | ||
+ | --> Rule Application: &Anonymous:Root([.DS_Store,AllData.txt],{}) | ||
+ | --> Distribution: &Anonymous:Root([.DS_Store,AllData.txt],{}) | ||
+ | >>> &Anonymous:Main([AllData.txt],{}) | ||
+ | ==> &Anonymous:Main([AllData.txt],{}) | ||
+ | --> Rule Application: &Anonymous:Main([AllData.txt],{}) | ||
+ | --> Distribution: &Anonymous:Main([AllData.txt],{}) | ||
+ | >>> &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data1"),line:(<i>1)}) | ||
+ | >>> &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data2"),line:(<i>2)}) | ||
+ | ==> &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data1"),line:(<i>1)}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH head -1 AllData.txt | tail -1 > Data1.txt | ||
+ | SH ------------------------------------------------------------ | ||
+ | >>> &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data3"),line:(<i>3)}) | ||
+ | <== &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data1"),line:(<i>1)}) | ||
+ | ==> &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data2"),line:(<i>2)}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH head -2 AllData.txt | tail -1 > Data2.txt | ||
+ | SH ------------------------------------------------------------ | ||
+ | <== &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data2"),line:(<i>2)}) | ||
+ | ==> &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data3"),line:(<i>3)}) | ||
+ | SH ------------------------------------------------------------ | ||
+ | SH head -3 AllData.txt | tail -1 > Data3.txt | ||
+ | SH ------------------------------------------------------------ | ||
+ | <== &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data3"),line:(<i>3)}) | ||
+ | <-- Distribution: &Anonymous:Main([AllData.txt],{}) | ||
+ | <-- Rule Application: &Anonymous:Main([AllData.txt],{}) | ||
+ | <-- Distribution: &Anonymous:Root([.DS_Store,AllData.txt],{}) | ||
+ | <== &Anonymous:Main([AllData.txt],{}) | ||
+ | <-- Rule Application: &Anonymous:Root([.DS_Store,AllData.txt],{}) | ||
+ | <== &Anonymous:Root([.DS_Store,AllData.txt],{}) | ||
+ | /Basic14$ cat process/output/Data1.txt | ||
+ | ABC | ||
+ | /Basic14$ cat process/output/Data2.txt | ||
+ | DEF | ||
+ | /Basic14$ cat process/output/Data3.txt | ||
+ | GHI | ||
+ | </pre> | ||
+ | 1, 2, 3行目の内容がそれぞれのファイルに出力されました。<br> | ||
+ | |||
+ | .except($I[1])を除いた場合は発火しません。<br> | ||
+ | <pre> | ||
+ | /Basic14$ pione-client DivideText.pione -i DivideTextInput/ | ||
+ | ==> &Anonymous:Root([.DS_Store,AllData.txt],{}) | ||
+ | --> Rule Application: &Anonymous:Root([.DS_Store,AllData.txt],{}) | ||
+ | <-- Rule Application: &Anonymous:Root([.DS_Store,AllData.txt],{}) | ||
+ | <== &Anonymous:Root([.DS_Store,AllData.txt],{}) | ||
+ | </pre> | ||
+ | |||
+ | また、exceptionsを使用すると設定している例外をリストで得ることができます。<br> | ||
+ | <pre> | ||
+ | Rule Main | ||
+ | input '1.txt' | ||
+ | input '2.txt' | ||
+ | input '3.txt' | ||
+ | output '*.txt'.all.except($I[1] or $I[2] or $I[3]) | ||
+ | Action | ||
+ | for data in {$O[1].exceptions()} | ||
+ | do | ||
+ | cat $data >> AllData.txt | ||
+ | wc $data >> AllInfo.txt | ||
+ | done | ||
+ | End | ||
+ | </pre> | ||
+ | 上記の例ではoutputに例外が$I[1] or $I[2] or $I[3]と設定されているので、{$O[1].exceptions()}では1.txt 2.txt 3.txtのリストとして得られるので、for文がこれらのファイルによって回ることになります。<br> | ||
+ | <br> | ||
+ | === 基本15(ループ文3) === | ||
+ | [[#基本10(ループ文)]]、[[#基本11(ループ文2)]]にてループ文を記述する方法を示しました。しかし、いずれも多くのルールを入れ子で呼ぶことになり、ループ数が多くなると入出力ファイルの引き継ぎも多くなり時間が掛かってしまいます。今回は一つのルールから全てのルールを呼び出すようにしてループを行うようにしてみます。<br> | ||
+ | <br> | ||
+ | |||
+ | 下記の整数型メソッドuptoを使用すると、1つのルールからパラメータを一気に設定して各々の値でルールを実行することができます。uptoは引数の値まで1ずつ加算しながら整数を返すメソッドです。(参照:[[PIONEの式#整数型(integer)]]) | ||
+ | <pre> | ||
+ | param $min := 0 | ||
+ | param $max := 64 | ||
+ | |||
+ | Rule Main | ||
+ | output '*.txt' | ||
+ | Flow | ||
+ | rule Test {val: $min.upto($max)} | ||
+ | End | ||
+ | |||
+ | Rule Test | ||
+ | output '{$val}.txt' | ||
+ | param $val | ||
+ | Action | ||
+ | touch {$O[1]} | ||
+ | End | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | またdowntoを使用する方法もあります。uptoは引数の値まで1ずつ減算しながら整数を返すメソッドです。(参照:[[PIONEの式#整数型(integer)]])<br> | ||
+ | <pre> | ||
+ | rule Test {val: $max.downto($min)} | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | uptoを利用すると、[[#基本10(ループ文)]]は下記のようになります。<br> | ||
+ | <pre> | ||
+ | param $maxval := 456 | ||
+ | param $minval := 123 | ||
+ | param $dval := 37 | ||
+ | |||
+ | Rule Main | ||
+ | output '*.txt'.all | ||
+ | Flow | ||
+ | rule EvenOdd {val: $minval + ($dval * (0.upto(($maxval - $minval)/$dval)))} | ||
+ | End | ||
+ | |||
+ | Rule EvenOdd | ||
+ | output '*.txt' | ||
+ | param $val | ||
+ | Flow | ||
+ | if $val % 2 == 0 | ||
+ | rule Even {val: $val} | ||
+ | else | ||
+ | rule Odd {val: $val} | ||
+ | end | ||
+ | End | ||
+ | |||
+ | Rule Even | ||
+ | output '*.txt' | ||
+ | param $val | ||
+ | Action | ||
+ | echo "{$val} is even." >> Even{$val}.txt; | ||
+ | End | ||
+ | |||
+ | Rule Odd | ||
+ | output '*.txt' | ||
+ | Action | ||
+ | echo "{$val} is odd." >> Odd{$val}.txt; | ||
+ | End | ||
+ | </pre>このファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic15 こちら]からダウンロードできます。 | ||
+ | <br> | ||
+ | |||
+ | valに設定する式が複雑な場合は下記のように一旦、[[変数束縛]]で計算したものを利用する方法もあります。<br> | ||
+ | <pre> | ||
+ | param $maxval := 456 | ||
+ | param $minval := 123 | ||
+ | param $dval := 37 | ||
+ | $loop := ($maxval - $minval)/$dval | ||
+ | |||
+ | Rule Main | ||
+ | output '*.txt'.all | ||
+ | Flow | ||
+ | rule EvenOdd {val: $minval + ($dval * (0.upto($loop))} | ||
+ | End | ||
+ | |||
+ | -以下略- | ||
+ | </pre> | ||
+ | 上記の場合は変数loopにパラメータから計算したループ数を設定しています。<br> | ||
+ | <br> | ||
+ | === 基本16(ランダム) === | ||
+ | メソッドrandomを使用するとランダムに選んだファイルを出力することができます。(現v0.5.0では未実装)<br> | ||
+ | <br> | ||
+ | |||
+ | <pre> | ||
+ | Rule Main | ||
+ | input '*.txt' | ||
+ | output '*.out'.all.random | ||
+ | Action | ||
+ | max=$(wc -l {$I[1]} | awk '{print $1}') | ||
+ | for (( i=1; i<${max}; i++ )) | ||
+ | do | ||
+ | head -${i} {$I[1]} | tail -1 > {$I[1][1]}-{$i}.out | ||
+ | done | ||
+ | End | ||
+ | </pre> | ||
+ | 上記の例では入力.txtファイルからランダムに選んだ行数を.outファイルとして出力します。このファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Basic16 こちら]からダウンロードできます。<br> | ||
<br> | <br> | ||
== 応用 == | == 応用 == | ||
− | + | さて、ここまでの基本を組み合わせて、[[Eosのコマンド]]を使用する問題を考えてみましょう。<br> | |
<br> | <br> | ||
=== 応用1(フローの制御)=== | === 応用1(フローの制御)=== | ||
==== 問題1 ==== | ==== 問題1 ==== | ||
− | <div>*. | + | <div>*.mrc([[mrcImage]])と*.prametersで指定される入力ファイルがあります。</div> |
− | <div>*. | + | <div>それぞれの*.mrcファイルから最大値を中心として切り出した*.roiを出力するCenterGet.pioneを考えてみましょう。<br> |
但し、*.parametersは以下のフォーマットで切り出しサイズ(Sx, Sy, Sz)を格納しているファイルとします。</div> | 但し、*.parametersは以下のフォーマットで切り出しサイズ(Sx, Sy, Sz)を格納しているファイルとします。</div> | ||
<br> | <br> | ||
行1,280: | 行2,402: | ||
==== 問題2 ==== | ==== 問題2 ==== | ||
− | 2Dファイル([[mrcImage]])を入力ファイルとし、ローパスフィルタ([[mrcImageLowPassFilter]]) | + | 2Dファイル([[mrcImage]])を入力ファイルとし、ローパスフィルタ([[mrcImageLowPassFilter]])をウェブページの操作によって実行する次のコマンドを考えます。 |
+ | *入力ファイルはファイル選択画面から自由に選べるようにし、オプションの値も画面操作で設定できるようにします。 | ||
+ | *コマンド実行後は入力ファイルと出力ファイルの画像ファイルをそれぞれ表示して比較できるようにします。 | ||
+ | *この画面を見ながら再設定するか、終了するかを選び、終了後に最後の変換ファイルを出力ファイルとして得られるようにします。<br> | ||
<br> | <br> | ||
行1,291: | 行2,416: | ||
==== 解答例 ==== | ==== 解答例 ==== | ||
− | [[ | + | [[PIONE定義書]]、[[html]]ファイル、[[cgi]]ファイルをそれぞれ作成します。<br> |
<br> | <br> | ||
− | ===== [[ | + | ===== [[PIONE定義書]] ===== |
メイン(Main.pione)の処理を定義します。今回は設定のほとんどをウェブページに委ねるのでここでは出力ファイル名のみ定義しています。<br> | メイン(Main.pione)の処理を定義します。今回は設定のほとんどをウェブページに委ねるのでここでは出力ファイル名のみ定義しています。<br> | ||
<pre> | <pre> | ||
行1,565: | 行2,690: | ||
<br> | <br> | ||
− | ==== | + | これらのファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Advanced2 こちら]からダウンロードできます。<br> |
− | + | <br> | |
+ | |||
+ | ==== [[パッケージ作成]] ==== | ||
+ | コードが完成したら[[パッケージ作成]]を行います。<br> | ||
<pre> | <pre> | ||
/PIONE$ pione package build Advanced2 | /PIONE$ pione package build Advanced2 | ||
行1,576: | 行2,704: | ||
==== 実行結果 ==== | ==== 実行結果 ==== | ||
では、動作させてみましょう。<br> | では、動作させてみましょう。<br> | ||
− | [[ | + | [[PIONE Webclient]]のジョブページで実行し、Interactionを選択してindexページを開きます。ここでmrcImageLowPassFilterを選択すると設定画面が表示されます。ここでは入力ファイルや設定を変更しながらコマンドの実行を試すことができます。終了やCloseを選択すると、最後に処理を行った結果で出力ファイルが残ります。また、出力ファイル名を変更すると複数のファイルを残すことができます。<br> |
<br> | <br> | ||
行1,604: | 行2,732: | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
− | <td>[[画像:Outdata-PIONE-Advanced2-1.png]]</td> | + | <td>[[画像:Outdata-PIONE-Advanced2-1.png|border]]</td> |
<td>></td> | <td>></td> | ||
− | <td>[[画像:Outdata1-PIONE-Advanced2-1.png]]</td> | + | <td>[[画像:Outdata1-PIONE-Advanced2-1.png|border]]</td> |
</tr> | </tr> | ||
<tr> | <tr> | ||
行1,612: | 行2,740: | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
− | <td>[[画像:Outdata-PIONE-Advanced2-2.png]]</td> | + | <td>[[画像:Outdata-PIONE-Advanced2-2.png|border]]</td> |
<td>></td> | <td>></td> | ||
− | <td>[[画像:Outdata1-PIONE-Advanced2-2.png]]</td> | + | <td>[[画像:Outdata1-PIONE-Advanced2-2.png|border]]</td> |
</tr> | </tr> | ||
<tr> | <tr> | ||
行1,620: | 行2,748: | ||
</tr> | </tr> | ||
<tr> | <tr> | ||
− | <td>[[画像:Outdata-PIONE-Advanced2-3.png]]</td> | + | <td>[[画像:Outdata-PIONE-Advanced2-3.png|border]]</td> |
<td>></td> | <td>></td> | ||
− | <td>[[画像:Outdata1-PIONE-Advanced2-3.png]]</td> | + | <td>[[画像:Outdata1-PIONE-Advanced2-3.png|border]]</td> |
</tr> | </tr> | ||
</table> | </table> | ||
<br> | <br> | ||
− | 終了後にResult Filesからファイルをダウンロードすると、outputディレクトリにて最後に変換した.lpfファイルを得ることができます。あるいはこの[[ | + | 終了後にResult Filesからファイルをダウンロードすると、outputディレクトリにて最後に変換した.lpfファイルを得ることができます。あるいはこの[[PIONE定義書]]に処理を追加して、作成した.lpfファイルを入力ファイルとして使用することもできます。<br> |
<br> | <br> | ||
− | ==== | + | ==== 補足(不要ファイルの削除) ==== |
− | + | 今回の処理は操作によって自由にファイルが作成できます。しかし、中には不要なファイルも出てくる場合もあります。そこで、補足処理としてサーバ上のファイルリストを表示し、不要ファイルを削除できるようにしてみましょう。新しく下記のファイルを追加します。なお、pione-action=...については[[インタラクションAPI]]に詳細を記載しています。<br> | |
<br> | <br> | ||
− | ==== | + | /etc/FileDelete.html<br> |
− | pione-action= | + | <pre> |
+ | <!DOCTYPE html> | ||
+ | <html> | ||
+ | <head> | ||
+ | <meta charset="utf-8"> | ||
+ | <title>InteractiveCommand Index Page</title> | ||
+ | </head> | ||
+ | <body> | ||
+ | <a href="index.html">戻る</a><br> | ||
+ | <a href="?pione-action=finish">終了</a><br> | ||
+ | <br> | ||
+ | <form action="FileDelete.cgi" method="post" enctype="multipart/form-data"> | ||
+ | 削除ファイル名<br> | ||
+ | <input type="text" name="DeleteFile" value=""/> | ||
+ | <button type="submit">削除</button> | ||
+ | </form> | ||
+ | ファイルリスト<br> | ||
+ | <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script> | ||
+ | <div id="textDiv"></div> | ||
+ | <script type="text/javascript"> | ||
+ | var div = document.getElementById("textDiv"); | ||
+ | div.textContent = ""; | ||
+ | $.getJSON("./", {"pione-action": "list"}, function(data){ | ||
+ | $.each(data, function() { | ||
+ | strOut = ".lpf" | ||
+ | if( this.name.indexOf(strOut) + strOut.length == this.name.length ){ | ||
+ | div.textContent += this.name +"\n"; | ||
+ | } | ||
+ | }); | ||
+ | }); | ||
+ | </script> | ||
+ | </body> | ||
+ | </html> | ||
+ | </pre> | ||
+ | ファイルリストではpione-action=listによって得たファイルリストの中から最後が.lpfで終わるファイルのみを出力するようにしています。このリストを見ながら削除するファイルをテキストボックスに書き込んで、削除ボタンを押すと削除処理(FileDelete.cgi)が行われます。<br> | ||
<br> | <br> | ||
− | ===== | + | /bin/FileDelete.cgi<br> |
− | pione-action= | + | <pre> |
+ | #!/usr/bin/env ruby | ||
+ | |||
+ | require 'cgi' | ||
+ | |||
+ | cgi = CGI.new | ||
+ | |||
+ | ### Header | ||
+ | HTMLstr = "" | ||
+ | HTMLstr += "<!DOCTYPE html>" | ||
+ | HTMLstr += "<html>" | ||
+ | HTMLstr += "<head>" | ||
+ | HTMLstr += "<title>FileDelete</title>" | ||
+ | |||
+ | ## Query to Parameters | ||
+ | strDeleteFile = cgi['DeleteFile'] | ||
+ | |||
+ | ## View as HTML Statement | ||
+ | HTMLstr += '<meta http-equiv="refresh" content="0;URL=./' + strDeleteFile + '?pione-action=delete">' | ||
+ | |||
+ | HTMLstr += "</head>" | ||
+ | HTMLstr += "</html>" | ||
+ | |||
+ | # Output as html | ||
+ | cgi.out(type: "text/html") do | ||
+ | HTMLstr | ||
+ | end | ||
+ | </pre> | ||
+ | ヘッダにてDeleteFileを削除する操作(pione-action=delete)を行っています。<br> | ||
<br> | <br> | ||
− | + | さらに出来上がったページへアクセスするためにindex.htmlのbody内に下記の内容を追加します。<br> | |
− | + | <pre> | |
+ | <a href="FileDelete.html">FileDelete</a><br> | ||
+ | </pre> | ||
<br> | <br> | ||
− | + | これにより不要ファイルの削除を行うことができます。<br> | |
− | + | [[画像:Outdata-PIONE-Advanced2-4.png|border]]<br> | |
+ | 上記の例ではOutdata.lpfを削除しています。<br> | ||
+ | <br> | ||
+ | [[画像:Outdata-PIONE-Advanced2-5.png|border]]<br> | ||
+ | 削除完了画面が表示されるので、ブラウザのバックボタンで戻ります。<br> | ||
+ | <br> | ||
+ | [[画像:Outdata-PIONE-Advanced2-6.png|border]]<br> | ||
+ | 画面表示を更新するとOutdata.lpfが消えていることが確認できます。また、インタラクティブ操作を終了し、ファイルをダウンロードするとOudata.lpfが無いことも確認できます。これにより不要ファイルを削除できたことが分かりました。<br> | ||
<br> | <br> | ||
− | ===== | + | === 応用3(参照像の作成) === |
− | pione-action= | + | 今回はループ文を使用した例を示します。<br> |
+ | <br> | ||
+ | |||
+ | ==== 問題3 ==== | ||
+ | 入力ファイルを参照用の3次元画像(.ref3d)として、指定した範囲の角度で参照像(.ref2d)を作成する処理を考えます。[[PIONE Webclient]]を利用し、作成した各参照像の画像を確認しながら、ユーザ操作によって適切な角度を決定できるようにします。<br> | ||
+ | <br> | ||
+ | |||
+ | ----- | ||
+ | |||
+ | 応用問題3:上記の機能を持つパッケージRef3DtoRef2D.ppgを作成してみましょう。 | ||
+ | |||
+ | ----- | ||
+ | <br> | ||
+ | |||
+ | ==== 解答例 ==== | ||
+ | ===== [[PIONE定義書]] ===== | ||
+ | ユーザ操作によって3次元画像と投影する角度を設定し、そこから参照像を作成して、結果を画像として確認し、決定かやり直しかを選択できる流れとしています。<br> | ||
+ | <br> | ||
+ | |||
+ | Annotation.pione<br> | ||
+ | <pre> | ||
+ | .@ PackageName :: "Ref3DtoRef2D" | ||
+ | .@ Editor :: "Kinoshita" | ||
+ | .@ Tag :: "v0.2.0" | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | Main.pione | ||
+ | <pre> | ||
+ | Rule Main | ||
+ | output '*.ref3d' | ||
+ | output '*.ref2d' | ||
+ | output '*.mon' | ||
+ | output '*.gif' | ||
+ | Flow | ||
+ | rule Start | ||
+ | rule SubMain | ||
+ | rule Result | ||
+ | rule Finish | ||
+ | End | ||
+ | |||
+ | Rule Start | ||
+ | output 'Start0!Flag' | ||
+ | Action | ||
+ | touch {$O[1]} | ||
+ | End | ||
+ | |||
+ | Rule SubMain | ||
+ | input 'Start*!Flag' | ||
+ | output '*.*'.all | ||
+ | Flow | ||
+ | rule Interaction {val : '{$I[1][1]}'.str().i()} | ||
+ | rule Ref3DtoRef2D {val : '{$I[1][1]}'.str().i()} | ||
+ | End | ||
+ | |||
+ | Rule Finish | ||
+ | input 'Finish*!Flag' | ||
+ | input '*.*-{$I[1][1]}' | ||
+ | output '{$I[2][1]}.{$I[2][2]}' | ||
+ | Action | ||
+ | cp {$I[2]} {$O[1]} | ||
+ | End | ||
+ | </pre> | ||
+ | SubMainルールにて角度と3次元画像の設定とその2次元画像の作成を実行します。このルールはStartN!Flagで発火し、最初はStartルールで、やり直すときはResultルールでそれぞれStartN!Flagを作成します。このときのNはやり直しのために設定した整数で、前に作成されたファイルとこれから作成するファイルが混ざらないようにするために設定しています。ルールFinishで決定したときのファイルのみを取り出します。<br> | ||
+ | <br> | ||
+ | |||
+ | Interaction.pione<br> | ||
+ | <pre> | ||
+ | Rule Interaction | ||
+ | input 'Start{$val}!Flag' | ||
+ | output '*.*'.all | ||
+ | param $val | ||
+ | Action | ||
+ | # build public directory for pione-interactive | ||
+ | mkdir public | ||
+ | cp etc/* public | ||
+ | cp bin/* public | ||
+ | |||
+ | # start interactive operation | ||
+ | pione-interactive browser --public public | ||
+ | |||
+ | cp public/* . | ||
+ | for file in $(ls *Mode *Rot1 *Rot2 *Rot3 *.ref3d) ; | ||
+ | do | ||
+ | mv "$file" "$file-{$val}"; | ||
+ | done | ||
+ | End | ||
+ | </pre> | ||
+ | やり直しに対応できるように処理の最後で作成されたファイルに番号を付けています。<br> | ||
+ | <br> | ||
+ | |||
+ | Ref3DtoRef2D.pione<br> | ||
+ | <pre> | ||
+ | Rule Ref3DtoRef2D | ||
+ | input '*!*!*.!Rot1-{$val}' | ||
+ | input '*!*!*.!Rot2-{$val}' | ||
+ | input '*!*!*.!Rot3-{$val}' | ||
+ | input '*.!Mode-{$val}' | ||
+ | input '*.ref3d-{$val}' | ||
+ | output '*.ref2d-{$val}'.all | ||
+ | output 'Ref3DtoRef2D.info-{$val}' | ||
+ | $min1 := '{$I[1][1]}'.str().f() | ||
+ | $max1 := '{$I[1][2]}'.str().f() | ||
+ | $delta1 := '{$I[1][3]}'.str().f() | ||
+ | $loop1 := (($max1 - $min1) / $delta1).i() | ||
+ | $min2 := '{$I[2][1]}'.str().f() | ||
+ | $max2 := '{$I[2][2]}'.str().f() | ||
+ | $delta2 := '{$I[2][3]}'.str().f() | ||
+ | $loop2 := (($max2 - $min2) / $delta2).i() | ||
+ | $min3 := '{$I[3][1]}'.str().f() | ||
+ | $max3 := '{$I[3][2]}'.str().f() | ||
+ | $delta3 := '{$I[3][3]}'.str().f() | ||
+ | $loop3 := (($max3 - $min3) / $delta3).i() | ||
+ | $mode := '{$I[4][1]}'.str() | ||
+ | param $val | ||
+ | Flow | ||
+ | rule One3Dto2D {rot1: $min1 + ((0.upto($loop1)).f() * $delta1), rot2: $min2 + ((0.upto($loop2)).f() * $delta2), rot3: $min3 + ((0.upto($loop3)).f() * $delta3), mode : $mode, val : $val} | ||
+ | rule Info3Dto2D {val : $val} | ||
+ | End | ||
+ | |||
+ | Rule Info3Dto2D | ||
+ | input '*.ref2d-{$val}'.all | ||
+ | output 'Ref3DtoRef2D.info-{$val}' | ||
+ | param $val | ||
+ | Action | ||
+ | for file in $(ls *.ref2d-{$val}) | ||
+ | do | ||
+ | echo "./$file" >> {$O[1]} | ||
+ | done | ||
+ | End | ||
+ | |||
+ | Rule One3Dto2D | ||
+ | input '*.ref3d-{$val}' | ||
+ | output '*.ref2d-{$val}'.all | ||
+ | param $rot1 | ||
+ | param $rot2 | ||
+ | param $rot3 | ||
+ | param $mode | ||
+ | param $val | ||
+ | Action | ||
+ | output="{$I[1][1]}-{$mode}-{$rot1}-{$rot2}-{$rot3}.ref2d-{$val}" | ||
+ | mrc3Dto2D -i {$I[1]} -o $output \ | ||
+ | -Rot1 {$rot1} {$rot1} 1 \ | ||
+ | -Rot2 {$rot2} {$rot2} 1 \ | ||
+ | -Rot3 {$rot3} {$rot3} 1 \ | ||
+ | -EulerMode {$mode}; | ||
+ | End | ||
+ | </pre> | ||
+ | ルールRef3DtoRef2Dは大きくFlowLoop3Dto2D1とOne3Dto2Dの2つのルールに分かれます。ここでもやり直しに対応するためにファイルの末尾に番号を付けます。ルールInfo3Dto2Dでは画像作成のために2次元画像の一覧を作成します。ルールOne3Dto2D1ではuptoによるループを利用して3次元画像から[[mrc3Dto2D]]によって2次元画像を次々に作成します。このとき3軸毎に回転角度をそれぞれuptoで設定しています。<br> | ||
+ | <br> | ||
+ | |||
+ | Result.pione<br> | ||
+ | <pre> | ||
+ | Rule Result | ||
+ | input 'Ref3DtoRef2D.info-*' | ||
+ | input '*.ref2d-{$I[1][1]}'.all | ||
+ | output '*!Flag' | ||
+ | output 'Ref3DtoRef2D.mon-{$I[1][1]}' | ||
+ | output 'Ref3DtoRef2D.gif-{$I[1][1]}' | ||
+ | Flow | ||
+ | rule Ref2DtoGIF {val : '{$I[1][1]}'.str().i()} | ||
+ | rule ResultCheck {val : '{$I[1][1]}'.str().i()} | ||
+ | End | ||
+ | |||
+ | Rule Ref2DtoGIF | ||
+ | input 'Ref3DtoRef2D.info-{$val}' | ||
+ | input '*.ref2d-{$val}'.all | ||
+ | output 'Ref3DtoRef2D.mon-{$val}' | ||
+ | output 'Ref3DtoRef2D.gif-{$val}' | ||
+ | param $val | ||
+ | Action | ||
+ | mrcImageMontageCreate -i {$I[1]} -o {$O[1]} | ||
+ | mrc2gif -i {$O[1]} -o {$O[2]} | ||
+ | End | ||
+ | |||
+ | Rule ResultCheck | ||
+ | input 'Ref3DtoRef2D.gif-{$val}' | ||
+ | output '*!Flag' | ||
+ | param $val | ||
+ | Action | ||
+ | # build public directory for pione-interactive | ||
+ | mkdir public | ||
+ | cp etc/index2.html public/index.html | ||
+ | cp bin/* public | ||
+ | cp {$I[1]} public/Ref3DtoRef2D.gif | ||
+ | |||
+ | # start interactive operation | ||
+ | pione-interactive browser --public public | ||
+ | |||
+ | cp public/* . | ||
+ | if [ -e "Finish!Flag" ]; then | ||
+ | mv "Finish!Flag" "Finish{$val}!Flag"; | ||
+ | elif [ -e "Start!Flag" ]; then | ||
+ | mv "Start!Flag" "Start{$val + 1}!Flag"; | ||
+ | fi | ||
+ | End | ||
+ | </pre> | ||
+ | ルールResultはRef2DtoGIFとResultCheckに分かれます。ルールRef2DtoGIFでは各2次元像をひとまとめにしたgif画像を作成します。ルールResultCheckではgif画像をindex2.htmlのページに表示して、ユーザ操作にて決定のときはFinishN!Flagをやり直しのときはStartN!Flagを作成するようにしています。<br> | ||
+ | <br> | ||
+ | |||
+ | ===== [[html]]ファイル ===== | ||
+ | index.html<br> | ||
+ | <pre> | ||
+ | <!DOCTYPE html> | ||
+ | <html> | ||
+ | <head> | ||
+ | <meta charset="utf-8"> | ||
+ | <title>InteractiveCommand Index Page</title> | ||
+ | </head> | ||
+ | <body> | ||
+ | <a href="Ref3DtoRef2D.html">Ref3DtoRef2D</a><br> | ||
+ | <br> | ||
+ | <a href="?pione-action=finish">終了</a> | ||
+ | </body> | ||
+ | </html> | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | Ref3DtoRef2D.html<br> | ||
+ | <pre> | ||
+ | <!DOCTYPE html> | ||
+ | <html> | ||
+ | <head> | ||
+ | <meta charset="utf-8"> | ||
+ | <title>Ref3DtoRef2D</title> | ||
+ | </head> | ||
+ | <body> | ||
+ | <form action="Ref3DtoRef2D.cgi" method="post" enctype="multipart/form-data"> | ||
+ | <button type="submit">開始</button> | ||
+ | <br> | ||
+ | <table> | ||
+ | <tr> | ||
+ | <td> | ||
+ | <input type="file" name="InputFile"/> | ||
+ | </td> | ||
+ | <td> | ||
+ | 入力ファイル名(.ref3d) | ||
+ | </td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td> | ||
+ | <select name="Mode1"> | ||
+ | <option value="X">X</option> | ||
+ | <option value="Y">Y</option> | ||
+ | <option value="Z">Z</option> | ||
+ | </select> | ||
+ | <select name="Mode2"> | ||
+ | <option value="O">O</option> | ||
+ | <option value="E">E</option> | ||
+ | </select> | ||
+ | <select name="Mode3"> | ||
+ | <option value="Y">Y</option> | ||
+ | <option value="N">N</option> | ||
+ | </select> | ||
+ | <select name="Mode4"> | ||
+ | <option value="S">S</option> | ||
+ | <option value="R">R</option> | ||
+ | </select> | ||
+ | </td> | ||
+ | <td> | ||
+ | 回転モード | ||
+ | </td> | ||
+ | </tr> | ||
+ | </table> | ||
+ | <table> | ||
+ | <tr> | ||
+ | <td> | ||
+ | |||
+ | </td> | ||
+ | <td> | ||
+ | 最小値 | ||
+ | </td> | ||
+ | <td> | ||
+ | 最大値 | ||
+ | </td> | ||
+ | <td> | ||
+ | 加算値 | ||
+ | </td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td> | ||
+ | 角度1 | ||
+ | </td> | ||
+ | <td> | ||
+ | <input type="text" name="RotMin1" value="0"/> | ||
+ | </td> | ||
+ | <td> | ||
+ | <input type="text" name="RotMax1" value="359"/> | ||
+ | </td> | ||
+ | <td> | ||
+ | <input type="text" name="RotDelta1" value="30"/> | ||
+ | </td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td> | ||
+ | 角度2 | ||
+ | </td> | ||
+ | <td> | ||
+ | <input type="text" name="RotMin2" value="0"/> | ||
+ | </td> | ||
+ | <td> | ||
+ | <input type="text" name="RotMax2" value="359"/> | ||
+ | </td> | ||
+ | <td> | ||
+ | <input type="text" name="RotDelta2" value="30"/> | ||
+ | </td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td> | ||
+ | 角度3 | ||
+ | </td> | ||
+ | <td> | ||
+ | <input type="text" name="RotMin3" value="0"/> | ||
+ | </td> | ||
+ | <td> | ||
+ | <input type="text" name="RotMax3" value="0"/> | ||
+ | </td> | ||
+ | <td> | ||
+ | <input type="text" name="RotDelta3" value="30"/> | ||
+ | </td> | ||
+ | </tr> | ||
+ | </table> | ||
+ | </form> | ||
+ | <br> | ||
+ | <br> | ||
+ | <a href="index.html">戻る</a><br> | ||
+ | <a href="?pione-action=finish">終了</a><br> | ||
+ | </body> | ||
+ | </html> | ||
+ | </pre> | ||
+ | このページでは入力ファイルと回転モードと及び各角度毎の最小値、最大値、加算値を設定して実行できるようにしています。<br> | ||
+ | <br> | ||
+ | |||
+ | index2.html<br> | ||
+ | <pre> | ||
+ | <!DOCTYPE html> | ||
+ | <html> | ||
+ | <head> | ||
+ | <meta charset="utf-8"> | ||
+ | <title>InteractiveCommand Index Page</title> | ||
+ | </head> | ||
+ | <body> | ||
+ | <img src="Ref3DtoRef2D.gif"><br> | ||
+ | <br> | ||
+ | <form action="Result.cgi" method="post" enctype="multipart/form-data"> | ||
+ | <button type="submit">決定</button><br> | ||
+ | <input type="hidden" name="Result" value="Finish"/> | ||
+ | </form> | ||
+ | <form action="Result.cgi" method="post" enctype="multipart/form-data"> | ||
+ | <button type="submit">やり直し</button><br> | ||
+ | <input type="hidden" name="Result" value="Start"/> | ||
+ | </form> | ||
+ | </body> | ||
+ | </html> | ||
+ | </pre> | ||
+ | これはルールResultのときに画像を確認しながら、決定かやり直しかを選択するためのページです。<br> | ||
+ | <br> | ||
+ | |||
+ | ===== [[cgi]]ファイル ===== | ||
+ | Ref3DtoRef2D.cgi<br> | ||
+ | <pre> | ||
+ | #!/usr/bin/env ruby | ||
+ | |||
+ | require 'cgi' | ||
+ | |||
+ | cgi = CGI.new | ||
+ | |||
+ | ### Header | ||
+ | strHTML = "" | ||
+ | strHTML += "<!DOCTYPE html>" | ||
+ | strHTML += "<html>" | ||
+ | strHTML += "<head>" | ||
+ | strHTML += "<title>Ref3DtoRef2D</title>" | ||
+ | ### Auto Close | ||
+ | strHTML += '<meta http-equiv="REFRESH" content="0;URL=?pione-action=finish">' | ||
+ | |||
+ | # Main Process | ||
+ | |||
+ | ## Query to Parameters | ||
+ | |||
+ | ### Copy InputFile | ||
+ | fpQueryInputFile = cgi.params['InputFile'][0] | ||
+ | strInputFile = fpQueryInputFile.original_filename | ||
+ | fpInputFile = open(strInputFile, "wb") | ||
+ | fpInputFile.write(fpQueryInputFile.read) | ||
+ | fpInputFile.close | ||
+ | |||
+ | ### Other Query | ||
+ | strMode = cgi['Mode1'] + cgi['Mode2'] + cgi['Mode3'] + cgi['Mode4'] | ||
+ | strRotMin1 = cgi['RotMin1'] | ||
+ | strRotMax1 = cgi['RotMax1'] | ||
+ | strRotDelta1 = cgi['RotDelta1'] | ||
+ | strRotMin2 = cgi['RotMin2'] | ||
+ | strRotMax2 = cgi['RotMax2'] | ||
+ | strRotDelta2 = cgi['RotDelta2'] | ||
+ | strRotMin3 = cgi['RotMin3'] | ||
+ | strRotMax3 = cgi['RotMax3'] | ||
+ | strRotDelta3 = cgi['RotDelta3'] | ||
+ | |||
+ | ### Set Parameter to FileNames | ||
+ | strParamFile = strMode + ".!Mode" | ||
+ | command = "touch " + strParamFile | ||
+ | system(command) | ||
+ | |||
+ | strParamFile = strRotMin1 + "!" + strRotMax1 + "!" + strRotDelta1 + ".!Rot1" | ||
+ | command = "touch " + strParamFile | ||
+ | system(command) | ||
+ | |||
+ | strParamFile = strRotMin2 + "!" + strRotMax2 + "!" + strRotDelta2 + ".!Rot2" | ||
+ | command = "touch " + strParamFile | ||
+ | system(command) | ||
+ | |||
+ | strParamFile = strRotMin3 + "!" + strRotMax3 + "!" + strRotDelta3 + ".!Rot3" | ||
+ | command = "touch " + strParamFile | ||
+ | system(command) | ||
+ | |||
+ | command = "cp " + strInputFile + " Refer.ref3d" | ||
+ | system(command) | ||
+ | |||
+ | strHTML += "</head>" | ||
+ | strHTML += "</html>" | ||
+ | |||
+ | # Output as html | ||
+ | cgi.out(type: "text/html") do | ||
+ | strHTML | ||
+ | end | ||
+ | </pre> | ||
+ | Ref3DtoRef2D.htmlで設定したデータをファイルに保存します。処理後は自動的にインタラクティブ操作を終了し、以降は[[PIONE]]にてルールRef3DtoRef2Dを実行します。<br> | ||
+ | <br> | ||
+ | |||
+ | <pre> | ||
+ | #!/usr/bin/env ruby | ||
+ | |||
+ | require 'cgi' | ||
+ | |||
+ | cgi = CGI.new | ||
+ | |||
+ | ### Header | ||
+ | strHTML = "" | ||
+ | strHTML += "<!DOCTYPE html>" | ||
+ | strHTML += "<html>" | ||
+ | strHTML += "<head>" | ||
+ | strHTML += "<title>Result</title>" | ||
+ | ### Auto Close | ||
+ | strHTML += '<meta http-equiv="REFRESH" content="0;URL=?pione-action=finish">' | ||
+ | |||
+ | # Main Process | ||
+ | ## Query to Parameters | ||
+ | Result = cgi['Result'] | ||
+ | |||
+ | command = "touch " + Result + "!Flag" | ||
+ | system(command) | ||
+ | |||
+ | strHTML += "</head>" | ||
+ | strHTML += "</html>" | ||
+ | |||
+ | # Output as html | ||
+ | cgi.out(type: "text/html") do | ||
+ | strHTML | ||
+ | end | ||
+ | </pre> | ||
+ | index2.htmlの操作によって、決定またはやり直しを選択したときの処理です。決定のときはFinish!Flag、やり直しのときはStart!FLagを作成します。処理後は自動的にインタラクティブ操作を終了します。<br> | ||
+ | <br> | ||
+ | |||
+ | これらのファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Advanced3 こちら]からダウンロードできます。<br> | ||
+ | <br> | ||
+ | |||
+ | ==== [[パッケージ作成]] ==== | ||
+ | コードが完成したら[[パッケージ作成]]を行います。<br> | ||
+ | <pre> | ||
+ | /PIONE$ pione package build Advanced3 | ||
+ | info: update the package info file: local:/Eos/tutorial/SampleCode/PIONE/Advanced3/pione-package.json | ||
+ | info: Package local:/Eos/tutorial/SampleCode/PIONE/Ref3DtoRef2D(Kinoshita)+v0.1.1.ppg has been built successfully. | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | ==== 実行結果 ==== | ||
+ | では、動作させてみましょう。<br> | ||
+ | [[PIONE Webclient]]のジョブページで実行し、Interactionを選択してindexページを開きます。ここでRef3DtoRef2Dを選択すると設定画面が表示されます。<br> | ||
+ | |||
+ | 今回は[[Media:1VOM.mrc|下記の入力ファイル]]を3次元像として2次元画像を作成してみます。<br> | ||
+ | <table> | ||
+ | <tr> | ||
+ | <td><p align="Center">[[画像:Input-1VOM.png]]<br> | ||
+ | xy平面<br></p> | ||
+ | </td> | ||
+ | <td><p align="Center">[[画像:Input1-1VOM.png]]<br> | ||
+ | yz平面<br></p> | ||
+ | </td> | ||
+ | <td><p align="left">最小<br> | ||
+ | 最大<br> | ||
+ | 平均値<br> | ||
+ | 標準偏差<br> | ||
+ | 標準誤差<br></p> | ||
+ | </td> | ||
+ | <td><p align="left">0 (0, 0, 0)<br> | ||
+ | 3398.12 (23, 55, 41)<br> | ||
+ | 72.129<br> | ||
+ | 294.805<br> | ||
+ | 0.368507<br></p> | ||
+ | </td> | ||
+ | </tr> | ||
+ | </table> | ||
+ | <br> | ||
+ | |||
+ | [[画像:Outdata-PIONE-Advanced3-1.png|border]]<br> | ||
+ | 入力ファイル及び回転のモード(参照: [[オイラー角]])、角度の範囲を設定して開始ボタンを押します。<br> | ||
+ | <br> | ||
+ | |||
+ | [[画像:Outdata-PIONE-Advanced3-2.png|border]]<br> | ||
+ | すると、上記のようにインタラクティブ操作は完了して、再び[[PIONE]]にて2次元像の作成処理が動きます。(このページは閉じて構いません)<br> | ||
+ | <br> | ||
+ | |||
+ | [[画像:Outdata-PIONE-Advanced3-3.png|border]]<br> | ||
+ | 再びInteractionボタンが赤色になりますので、開くと作成された2次元像の一覧が画像にて表示されます。この内容を見ながら「決定」か「やり直し」を選択します。<br> | ||
+ | <br> | ||
+ | |||
+ | [[画像:Outdata-PIONE-Advanced3-2.png|border]]<br> | ||
+ | 同様にインタラクティブ操作が完了しますので、ページを閉じます。<br> | ||
+ | <br> | ||
+ | |||
+ | [[画像:Outdata-PIONE-Advanced3-4.png|border]]<br> | ||
+ | 「やり直し」を選択すると、再度設定画面を開くことができます。また、「決定」を選択するとこのときの結果がouputへ出力されます。このようにして結果を確認しながら適切な設定を選び直すことが出来ます。<br> | ||
+ | <br> | ||
+ | |||
+ | 上記の設定で決定したときのgif画像(縮小表示)<br> | ||
+ | [[画像:Outdata-PIONE-Advanced3-5.gif|500px]]<br> | ||
+ | <br> | ||
+ | |||
+ | ==== 補足(チケットを使ってルールを制御する) ==== | ||
+ | 設定、処理、確認のルールが同時に動いてはそれぞれの役割が満たせませんので、チケットを使用して制御を加えてみましょう。もちろん入力ファイルや出力ファイルの関係がうまく作れていれば同時に動くことはありません。今回は念のための処置として作成します。<br> | ||
+ | <br> | ||
+ | |||
+ | Main.pione<br> | ||
+ | <pre> | ||
+ | Rule Main | ||
+ | output '*.ref3d' | ||
+ | output '*.ref2d' | ||
+ | output '*.mon' | ||
+ | output '*.gif' | ||
+ | Flow | ||
+ | rule Start >>> SubMain >>> Result | ||
+ | rule Finish | ||
+ | End | ||
+ | |||
+ | Rule Start | ||
+ | output 'Start0!Flag' | ||
+ | Action | ||
+ | touch {$O[1]} | ||
+ | End | ||
+ | |||
+ | Rule SubMain | ||
+ | input 'Start*!Flag' | ||
+ | output '*.*'.all | ||
+ | Flow | ||
+ | rule Interaction {val : '{$I[1][1]}'.str().i()} >>> Ref3DtoRef2D {val : '{$I[1][1]}'.str().i()} | ||
+ | End | ||
+ | |||
+ | Rule Finish | ||
+ | input 'Finish*!Flag' | ||
+ | input '*.*-{$I[1][1]}' | ||
+ | output '{$I[2][1]}.{$I[2][2]}' | ||
+ | Action | ||
+ | cp {$I[2]} {$O[1]} | ||
+ | End | ||
+ | </pre> | ||
+ | MainルールとSubMainルールにて順番を指定しています。<br> | ||
+ | このファイルは[https://sourceforge.jp/projects/eos/scm/git/tutorial/archive/master/SampleCode/PIONE/Advanced3plus1 こちら]からダウンロードできます。<br> | ||
+ | <br> | ||
+ | |||
+ | === 応用4(単粒子解析) === | ||
+ | 次は[[Makefile]]をベースとして[[単粒子解析]]のための処理を作ってみましょう。<br> | ||
+ | <br> | ||
+ | |||
+ | ==== 問題4 ==== | ||
+ | 元のMakefileが下記であったとします。<br> | ||
+ | <pre> | ||
+ | .SUFFIXES: .roi .smooth .shrink .pad .fit .corinfo .3dinfo .3dwholeinfo .3d4sinfo .lst .3dlst .padlst .3d .ds6 .3dwholelst .3dwhole .wholeds6 .3d4s .4sds6 .3d4sinfolst | ||
+ | |||
+ | #SHELL=/bin/bash | ||
+ | |||
+ | |||
+ | REFERENCE=1J4Z-move-shrink.stack | ||
+ | |||
+ | .lst.padlst: | ||
+ | rm -f $*.padlst | ||
+ | for i in `cat $*.lst`; do \ | ||
+ | FILENAME=`basename $$i .roi`; \ | ||
+ | echo $$FILENAME; \ | ||
+ | make $$FILENAME.pad ; \ | ||
+ | echo $$FILENAME.pad >> $*.padlst; \ | ||
+ | done | ||
+ | |||
+ | .padlst.3dlst: | ||
+ | rm -f $*.3dlst | ||
+ | rm -f $*.3dwholelst | ||
+ | for i in `cat $*.padlst`; do \ | ||
+ | FILENAME=`basename $$i .pad`; \ | ||
+ | make $$FILENAME.fit ; \ | ||
+ | make $$FILENAME.3dinfo ; \ | ||
+ | cat $$FILENAME.3dinfo >> $*.3dlst ; \ | ||
+ | make $$FILENAME.3dwholeinfo ; \ | ||
+ | cat $$FILENAME.3dwholeinfo >> $*.3dwholelst ; \ | ||
+ | done | ||
+ | |||
+ | .padlst.3d4sinfolst: | ||
+ | rm -f $*.3d4sinfolst | ||
+ | for i in `cat $*.padlst`; do \ | ||
+ | FILENAME=`basename $$i .pad`; \ | ||
+ | make $$FILENAME.3d4sinfo ; \ | ||
+ | cat $$FILENAME.3d4sinfo >> $*.3d4sinfolst ; \ | ||
+ | done | ||
+ | |||
+ | |||
+ | .roi.smooth: | ||
+ | mrcImageSmoothing -i $*.roi -o $*.smooth -m 1 -r 4 | ||
+ | |||
+ | .smooth.shrink: | ||
+ | mrcImageShrink -i $*.smooth -o $*.shrink -S 8 | ||
+ | |||
+ | .shrink.pad: | ||
+ | mrcImagePad -i $*.shrink -o $*.pad -W 32 -H 32 | ||
+ | |||
+ | .pad.fit: | ||
+ | mrcImageAutoRotationCorrelation -i $*.pad -r $(REFERENCE) -fit $*.fit -O $*.corinfo -n 72 -m 18 -nRot1 72 -nRot2 72 -nRot3 1 > /dev/null | ||
+ | |||
+ | .fit.3dinfo: | ||
+ | awk '/Cor/ { print $$18,$$16,$$2,$$3,$$4,"0.0"}' $*.corinfo | sort -r | sed -e s/pad/fit/ > $*.3dinfolst | ||
+ | head -n 1 $*.3dinfolst | awk ' {print $$2,$$3,$$4,$$5,$$6,$$1'} > $*.3dinfo | ||
+ | |||
+ | .fit.3dwholeinfo: | ||
+ | awk '/Cor/ { print $$18,$$16,$$2,$$3,$$4,$$9,$$11,$$12}' $*.corinfo | sort -r | sed -e s/pad/shift/ > $*.3dwholeinfolst | ||
+ | head -n 1 $*.3dwholeinfolst | awk ' {print $$2,$$3,$$4,$$5,$$6,$$7,$$8,$$1'} > $*.3dwholeinfo | ||
+ | X=`awk '{print -8*$$6; }' $*.3dwholeinfo`; \ | ||
+ | Y=`awk '{print -8*$$7; }' $*.3dwholeinfo`; \ | ||
+ | echo $$X,$$Y; mrcImageShift -i $*.roi -o $*.shift -x $$X -y $$Y -z 0 | ||
+ | |||
+ | .3dlst.3d: | ||
+ | mrc2Dto3D -I $*.3dlst -o $*.3d -InterpolationMode 2 -Double -DoubleCounter $*.3dcounter -CounterThreshold 0.5 -m 1 -WeightMode 6 | ||
+ | |||
+ | .3d.ds6: | ||
+ | mrc2map -i $*.3d -o $*.ds6 -m 3 | ||
+ | |||
+ | .3dwholelst.3dwhole: | ||
+ | mrc2Dto3D -I $*.3dwholelst -o $*.3dwhole -InterpolationMode 2 -Double -DoubleCounter $*.3dwholecounter -CounterThreshold 0.5 -m 1 -WeightMode 6 | ||
+ | .3dwhole.wholeds6: | ||
+ | mrc2map -i $*.3dwhole -o $*.wholeds6 -m 3 | ||
+ | ln -sf $*.wholeds6 $*.whole.ds6 | ||
+ | |||
+ | .fit.3d4sinfo: | ||
+ | awk '/Cor/ { print $$18,$$16,$$2,$$3,$$4,$$9,$$11,$$12}' $*.corinfo | sort -r | sed -e s/pad/shift/ > $*.3d4sinfolst | ||
+ | head -n 1 $*.3d4sinfolst | awk ' {print $$2,$$3,$$4,$$5,$$6,$$7,$$8,$$1'} > $*.3d4sinfo | ||
+ | X=`awk '{print -4*$$6; }' $*.3d4sinfo`; \ | ||
+ | Y=`awk '{print -4*$$7; }' $*.3d4sinfo`; \ | ||
+ | echo $$X,$$Y; | ||
+ | mrcImageShrink -i $*.roi -o $*.4shrink -S 4 | ||
+ | mrcImagePad -i $*.4shrink -o $*.pad -W 64 -H 64 | ||
+ | mrcImageShift -i $*.roi -o $*.4shift -x $$X -y $$Y -z 0 | ||
+ | |||
+ | .3d4sinfolst.3d4s: | ||
+ | mrc2Dto3D -I $*.3d4sinfolst -o $*.3d4s -InterpolationMode 2 -Double -DoubleCounter $*.3d4scounter -CounterThreshold 0.5 -m 1 -WeightMode 6 | ||
+ | .3d4s.4sds6: | ||
+ | mrc2map -i $*.3d4s -o $*.4sds6 -m 3 | ||
+ | ln -sf $*.4sds6 $*.4s.ds6 | ||
+ | </pre> | ||
+ | 上記のMakefileは複数の電子顕微鏡画像(.roi)に対して、参照像を使って角度決定をした後に3次元再構成を実行する処理となっています。.roiファイルの一覧は.lstに書かれていて、参照像の2Dスタック(REFERENCEで設定)があることが前提となっています。このmakefileは相関マップ(.corinfo)を作成するときに1/8とした画像を使用することで速度の向上をはかっています。"make (.lstのファイル名).3d"で1/8サイズ、"make (.lstのファイル名).3dwhole"で1/1サイズ、"make (.lstのファイル名).3d4s"で1/4サイズの3次元像が作成されます。<br> | ||
+ | <br> | ||
+ | |||
+ | ----- | ||
+ | |||
+ | 応用問題4:上記の機能を持つパッケージSingleParticle_3DReconstruction.ppgを作成してみましょう。 | ||
+ | |||
+ | ----- | ||
+ | <br> | ||
+ | |||
+ | ==== 解答例1 ==== | ||
+ | 解答例を一部示します。全体は[[Media:PIONE-Advanced4-1.zip|こちら]]をご覧下さい。元の機能に加えて、サンプルの.roiファイルと参照像2Dスタック.stackを作成できるようにしています。この場合の入力ファイルは.mrcのみです。さらに、作成された3次元像を画像としてすぐに確認できるように3方向からの投影像を[[gif]]ファイルで出力しています。また、機能毎にフラグを設けて作成したいデータのみを作成できるようにしています。<br> | ||
+ | <br> | ||
+ | |||
+ | ===== 実装 ===== | ||
+ | Main.pione<br> | ||
+ | <pre> | ||
+ | Rule Main | ||
+ | if $Flag_ROI or $Flag_Ref | ||
+ | input '*.mrc' | ||
+ | end | ||
+ | |||
+ | if $Flag_ROI.not() | ||
+ | input '*.lst' | ||
+ | input '*.roi'.all | ||
+ | end | ||
+ | |||
+ | if $Flag_Ref.not() | ||
+ | input '*.stack' | ||
+ | end | ||
+ | |||
+ | if $Flag_3D | ||
+ | output '{$I[1][1]}.3dlst' | ||
+ | output '{$I[1][1]}.3d' | ||
+ | output '{$I[1][1]}.ds6' | ||
+ | end | ||
+ | |||
+ | if $Flag_3DWhole | ||
+ | output '{$I[1][1]}.3dwholelst' | ||
+ | output '{$I[1][1]}.3dwhole' | ||
+ | output '{$I[1][1]}.wholeds6' | ||
+ | output '{$I[1][1]}.whole.ds6' | ||
+ | end | ||
+ | |||
+ | if $Flag_3D4S | ||
+ | output '{$I[1][1]}.3d4slst' | ||
+ | output '{$I[1][1]}.3d4s' | ||
+ | output '{$I[1][1]}.4sds6' | ||
+ | output '{$I[1][1]}.4s.ds6' | ||
+ | end | ||
+ | |||
+ | if $Flag_ROI | ||
+ | output '*.tiff'.all | ||
+ | end | ||
+ | |||
+ | output '*.gif'.all | ||
+ | |||
+ | Flow | ||
+ | |||
+ | if $Flag_ROI | ||
+ | rule Create_SampleROI | ||
+ | end | ||
+ | |||
+ | if $Flag_Ref | ||
+ | rule Create_stack | ||
+ | end | ||
+ | |||
+ | rule Preprocess | ||
+ | rule Create_fit | ||
+ | |||
+ | if $Flag_3D | ||
+ | rule Create_3dlst | ||
+ | rule Create_3d | ||
+ | end | ||
+ | |||
+ | if $Flag_3DWhole | ||
+ | rule Create_3dwholelst | ||
+ | rule Create_3dwhole | ||
+ | end | ||
+ | |||
+ | if $Flag_3D4S | ||
+ | rule Create_3d4slst | ||
+ | rule Create_3d4s | ||
+ | end | ||
+ | |||
+ | rule Projection_3d | ||
+ | End | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | Main.pione内で使用しているルールは下記のようにMakefileの処理と対応しています。<br> | ||
+ | <table border="1"> | ||
+ | <tr> | ||
+ | <th>Main.pione内のルール</th> | ||
+ | <th>サブルール</th> | ||
+ | <th>Makefile</th> | ||
+ | <th>説明</th> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>Create_SampleROI</td> | ||
+ | <td>-</td> | ||
+ | <td>なし</td> | ||
+ | <td>サンプル用の.roiファイルを複数作成</td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>Create_stack</td> | ||
+ | <td>-</td> | ||
+ | <td>なし</td> | ||
+ | <td>参照像の2Dスタック.stackファイルを作成</td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>Preprocess</td> | ||
+ | <td>pad_padlst<br> | ||
+ | roi_smooth<br> | ||
+ | smooth_shrink<br> | ||
+ | shrink_pad<br> | ||
+ | </td> | ||
+ | <td>.lst.padlst<br> | ||
+ | .roi.smooth<br> | ||
+ | .smooth.shrink<br> | ||
+ | .shrink.pad<br> | ||
+ | </td> | ||
+ | <td>.padファイル一覧を作成<br> | ||
+ | .roiの平滑化して.smoothを作成<br> | ||
+ | .smoothを縮小して.shrinkを作成<br> | ||
+ | .shrinkを参照画像と同じサイズにパディングして.padを作成<br> | ||
+ | </td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>Create_fit</td> | ||
+ | <td>-</td> | ||
+ | <td>.pad.fit</td> | ||
+ | <td>.padから.stackを使って相関マップ.corinfoを作成(最も近い画像.fitも作成)</td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>Create_3dlst</td> | ||
+ | <td>fit_3dinfo<br> | ||
+ | Sum_3dinfo_3dlst</td> | ||
+ | <td>.fit.3dinfo</td> | ||
+ | <td>各相関マップ.corinfoから相関値が最大の角度を集めてリスト.3dlstを作成</td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>Create_3d</td> | ||
+ | <td>Reconstruct_3dlst_3d<br> | ||
+ | Convert_3d_ds6</td> | ||
+ | <td>.3dlst.3d<br> | ||
+ | .3d.ds6</td> | ||
+ | <td>リスト.3dlstと.fitを使って3次元像.3d及び.ds6を作成(1/8サイズ)</td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>Create_3dwholelst</td> | ||
+ | <td>fit_3dwholeinfo<br> | ||
+ | Sum_3dwholeinfo_3dwholelst</td> | ||
+ | <td>.fit.3dwholeinfo</td> | ||
+ | <td>Create_3dlstの1/1サイズバージョン</td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>Create_3dwhole</td> | ||
+ | <td>Reconstruct_3dwholelst_3dwhole<br> | ||
+ | Convert_3dwhole_wholeds6</td> | ||
+ | <td>.3dwholelst.3dwhole<br> | ||
+ | .3dwhole.wholeds6</td> | ||
+ | <td>Create_3dの1/1サイズバージョン</td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>Create_3d4slst</td> | ||
+ | <td>fit_3d4sinfo<br> | ||
+ | Sum_3d4sinfo_3d4slst</td> | ||
+ | <td>.fit.3d4sinfo</td> | ||
+ | <td>Create_3dlstの1/4サイズバージョン</td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>Create_3d4s</td> | ||
+ | <td>Reconstruct_3d4slst_3d4s<br> | ||
+ | Convert_3d4s_4sds6</td> | ||
+ | <td>.3d4sinfolst.3d4s<br> | ||
+ | .3d4s.4sds6</td> | ||
+ | <td>Create_3dの1/4サイズバージョン</td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>Projection_3d</td> | ||
+ | <td>-</td> | ||
+ | <td>なし</td> | ||
+ | <td>作成された3次元像.3dなどを3方向から投影したgif画像を作成</td> | ||
+ | </tr> | ||
+ | </table> | ||
+ | <br> | ||
+ | |||
+ | また、下記のように画像サイズや回転方法をパラメータにて設定できるようにしています。<br> | ||
+ | <br> | ||
+ | |||
+ | Parameter.pione<br> | ||
+ | <pre> | ||
+ | # basic param | ||
+ | ## For Size | ||
+ | param $X_SIZE := 80 | ||
+ | param $Y_SIZE := 80 | ||
+ | param $SHRINK := 8 | ||
+ | param $SHRINK4S := 4 | ||
+ | |||
+ | ## For AutoRotationCorrelation | ||
+ | param $N_ROT := 72 | ||
+ | param $N_ROT1 := 72 | ||
+ | param $N_ROT2 := 72 | ||
+ | param $N_ROT3 := 1 | ||
+ | |||
+ | ## For RefData | ||
+ | param $REF_ROT_MODE := "XOYS" | ||
+ | param $REF_ROT1_START := 0 | ||
+ | param $REF_ROT1_END := 359 | ||
+ | param $REF_ROT1_DELTA := 15 | ||
+ | param $REF_ROT2_START := 0 | ||
+ | param $REF_ROT2_END := 359 | ||
+ | param $REF_ROT2_DELTA := 15 | ||
+ | param $REF_ROT3_START := 0 | ||
+ | param $REF_ROT3_END := 0 | ||
+ | param $REF_ROT3_DELTA := 15 | ||
+ | |||
+ | ## For SampleROI | ||
+ | param $ROI_ROT_MODE := "XOYS" | ||
+ | param $ROI_ROT1_START := 0 | ||
+ | param $ROI_ROT1_END := 359 | ||
+ | param $ROI_ROT1_DELTA := 30 | ||
+ | param $ROI_ROT2_START := 0 | ||
+ | param $ROI_ROT2_END := 359 | ||
+ | param $ROI_ROT2_DELTA := 30 | ||
+ | param $ROI_ROT3_START := 0 | ||
+ | param $ROI_ROT3_END := 0 | ||
+ | param $ROI_ROT3_DELTA := 30 | ||
+ | param $ROI_SN := 1 | ||
+ | |||
+ | # advanced param | ||
+ | advanced param $Flag_ROI := true | ||
+ | advanced param $Flag_Ref := true | ||
+ | advanced param $Flag_3D := true | ||
+ | advanced param $Flag_3DWhole := true | ||
+ | advanced param $Flag_3D4S := true | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | ===== Makefile との動作比較 ===== | ||
+ | 作成した[[PIONE定義書]]と元の[[Makefile]]との処理時間を比較して、ここまでの実装の効果があったかを検証します。今回は.3dファイルの作成に関して比較します。<br> | ||
+ | <br> | ||
+ | |||
+ | .roiファイルと.stackファイルは予めParameter.pioneを下記のようにして作成しています。([[ROI]]画像: サイズ256×256, 2つの角度0 ~ 300: 60刻み)<br> | ||
+ | <pre> | ||
+ | param $X_SIZE := 256 #80 | ||
+ | param $Y_SIZE := 256 #80 | ||
+ | |||
+ | param $ROI_ROT1_START := 0 | ||
+ | param $ROI_ROT1_END := 359 | ||
+ | param $ROI_ROT1_DELTA := 60 #30 | ||
+ | |||
+ | param $ROI_ROT2_START := 0 | ||
+ | param $ROI_ROT2_END := 359 | ||
+ | param $ROI_ROT2_DELTA := 60 #30 | ||
+ | |||
+ | param $ROI_ROT3_START := 0 | ||
+ | param $ROI_ROT3_END := 0 | ||
+ | param $ROI_ROT3_DELTA := 30 | ||
+ | |||
+ | advanced param $Flag_ROI := true | ||
+ | advanced param $Flag_Ref := true | ||
+ | advanced param $Flag_3D := false | ||
+ | advanced param $Flag_3DWhole := false | ||
+ | advanced param $Flag_3D4S := false | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | [[Media:1VOM.mrc|.roiや.stack作成で使用する.mrcファイル]]<br> | ||
+ | <table> | ||
+ | <tr> | ||
+ | <td><p align="Center">[[画像:Input-1VOM.png]]<br> | ||
+ | xy平面<br></p> | ||
+ | </td> | ||
+ | <td><p align="Center">[[画像:Input1-1VOM.png]]<br> | ||
+ | yz平面<br></p> | ||
+ | </td> | ||
+ | <td><p align="left">最小<br> | ||
+ | 最大<br> | ||
+ | 平均値<br> | ||
+ | 標準偏差<br> | ||
+ | 標準誤差<br></p> | ||
+ | </td> | ||
+ | <td><p align="left">0 (0, 0, 0)<br> | ||
+ | 3398.12 (23, 55, 41)<br> | ||
+ | 72.129<br> | ||
+ | 294.805<br> | ||
+ | 0.368507<br></p> | ||
+ | </td> | ||
+ | </tr> | ||
+ | </table> | ||
+ | <br> | ||
+ | |||
+ | 作成された[[ROI]]ファイルの一覧をData.lstに書き込んで[[Makefile]]で動作できるようにします。また、Makefile内にてREFERENCEを作成した.stackファイル名に変更します。<br> | ||
+ | <pre> | ||
+ | #REFERENCE=1J4Z-move-shrink.stack | ||
+ | REFERENCE=1WDC.stack | ||
+ | </pre> | ||
+ | 以上の.roiファイル、.stackファイル、Makefileを入力用のディレクトリに置きます。<br> | ||
+ | <br> | ||
+ | |||
+ | Makefileの処理時間の下記の[[PIONE定義書]]を作って、計測しています。<br> | ||
+ | 計測①コード<br> | ||
+ | <pre> | ||
+ | Rule Main | ||
+ | input '*.lst' | ||
+ | input '*.roi'.all | ||
+ | input 'Makefile' | ||
+ | input '*.stack'.all | ||
+ | output '{$I[1][1]}.ds6' | ||
+ | Action | ||
+ | make {$O[1]} | ||
+ | End | ||
+ | </pre> | ||
+ | 上記のルールの場合、(全体の処理)=(Makefile処理)+(入力ファイル処理)+(出力ファイル処理)となっているので、下記の計測②時間との差分を算出すれば、およそのMakefileの処理時間が算出できます。<br> | ||
+ | <br> | ||
+ | |||
+ | 計測②コード<br> | ||
+ | <pre> | ||
+ | Rule Main | ||
+ | input '*.lst' | ||
+ | input '*.roi'.all | ||
+ | input 'Makefile' | ||
+ | input '*.stack'.all | ||
+ | input '*.ds6' | ||
+ | output '{$I[5][1]}.ds6out' | ||
+ | Action | ||
+ | cp {$I[5]} {$O[1]} | ||
+ | End | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | 計測①結果(9分強)<br> | ||
+ | [[画像:Outdata-PIONE-Advanced4-12.png]]<br> | ||
+ | <br> | ||
+ | |||
+ | 計測②結果(1秒程度)<br> | ||
+ | [[画像:Outdata-PIONE-Advanced4-13.png]]<br> | ||
+ | <br> | ||
+ | |||
+ | 計測①が9分強、計測②が1秒程度なので、Makefileの処理時間は9分強掛かったことが分かりました。<br> | ||
+ | <br> | ||
+ | |||
+ | では、[[:Media:PIONE-Advanced4-1.zip|今回実装した処理]]をタスク数2(-t 2)で実行してみます。<br> | ||
+ | <br> | ||
+ | |||
+ | -t 2の結果(7分程度)<br> | ||
+ | [[画像:Outdata-PIONE-Advanced4-16.png]]<br> | ||
+ | 今回の実装においては2分程度の削減で、処理時間がMakefileの7/9となりました。しかし、この実装においてルールを細かく分けすぎているためにルール間の入出力ファイルのやり取りによる処理が各所で発生しています。次の項目にてこれらの無駄を省くためのコーディングを考えて、さらに時間を削減してみましょう。<br> | ||
+ | <br> | ||
+ | |||
+ | ==== 速度アップのための実装 ==== | ||
+ | ===== リスト作成の省略 ===== | ||
+ | まずは.lstなどを作成している部分について考えます。[[PIONE]]は1:複数の処理が可能ですのでリストの作成は省略できます。<br> | ||
+ | <br> | ||
+ | |||
+ | Main.pione<br> | ||
+ | <pre> | ||
+ | Rule Main | ||
+ | if ($Mode_ROI + $Mode_Ref) != 0 | ||
+ | input '*.mrc' | ||
+ | end | ||
+ | |||
+ | if $Mode_Ref == 0 | ||
+ | input '*.stack' | ||
+ | end | ||
+ | |||
+ | if $Mode_ROI == 0 | ||
+ | input '*.roi'.all | ||
+ | end | ||
+ | |||
+ | if $Flag_3D | ||
+ | output '{$I[1][1]}.3dlst' | ||
+ | output '{$I[1][1]}.3d' | ||
+ | output '{$I[1][1]}.ds6' | ||
+ | end | ||
+ | |||
+ | if $Flag_3DWhole | ||
+ | output '{$I[1][1]}.3dwholelst' | ||
+ | output '{$I[1][1]}.3dwhole' | ||
+ | output '{$I[1][1]}.wholeds6' | ||
+ | output '{$I[1][1]}.whole.ds6' | ||
+ | end | ||
+ | |||
+ | if $Flag_3D4S | ||
+ | output '{$I[1][1]}.3d4slst' | ||
+ | output '{$I[1][1]}.3d4s' | ||
+ | output '{$I[1][1]}.4sds6' | ||
+ | output '{$I[1][1]}.4s.ds6' | ||
+ | end | ||
+ | |||
+ | if $Mode_ROI != 0 | ||
+ | output '*.tiff'.all | ||
+ | end | ||
+ | |||
+ | if $Flag_Projection | ||
+ | output '*.gif'.all | ||
+ | end | ||
+ | Flow | ||
+ | case $Mode_ROI | ||
+ | when 1 | ||
+ | rule Create_Sample3d | ||
+ | rule Create_SampleROI_each {rot1: (0.upto($roi_rot1_loop))*$ROI_ROT1_DELTA, rot2: (0.upto($roi_rot2_loop))*$ROI_ROT2_DELTA, rot3: (0.upto($roi_rot3_loop))*$ROI_ROT3_DELTA} | ||
+ | when 2 | ||
+ | rule Create_Sample3d | ||
+ | rule Create_SampleROI_all | ||
+ | end | ||
+ | |||
+ | if $Mode_Ref != 0 | ||
+ | rule Create_stack | ||
+ | end | ||
+ | |||
+ | rule Preprocess | ||
+ | |||
+ | case $Mode_Cor | ||
+ | when 1 | ||
+ | rule Create_fit_each | ||
+ | when 2 | ||
+ | rule Create_fit_all | ||
+ | end | ||
+ | |||
+ | if $Flag_3D | ||
+ | rule Create_3dlst {filename: $I[1][1].str()} | ||
+ | rule Create_3d | ||
+ | end | ||
+ | |||
+ | if $Flag_3DWhole | ||
+ | rule Create_3dwholelst {filename: $I[1][1].str()} | ||
+ | rule Create_3dwhole | ||
+ | end | ||
+ | |||
+ | if $Flag_3D4S | ||
+ | rule Create_3d4slst {filename: $I[1][1].str()} | ||
+ | rule Create_3d4s | ||
+ | end | ||
+ | |||
+ | if $Flag_Projection | ||
+ | rule Projection_3d | ||
+ | end | ||
+ | End | ||
+ | </pre> | ||
+ | .roiファイル、.padファイルの一覧を記述した.lstファイル.padlstファイルを廃止して、入力用ディレクトリにある全ての.roiファイルについて処理を行うように変更しました。(但し、.3dlstなどは角度情報として使用するのでそのまま)また、入力ファイル1つに対して複数のファイルを作成する箇所についてeachで行うかallで行うかをパラメータによって決められるようにしています。今回の場合はeachを使用して複数のタスクで実行すると速度の改善が期待できます。(参照:[[#基本4の補足(eachでの速度検証)]])<br> | ||
+ | <br> | ||
+ | |||
+ | allで実行した場合(約65秒)<br> | ||
+ | [[画像:Outdata-PIONE-Advanced4-7.png|1120px]]<br> | ||
+ | <br> | ||
+ | |||
+ | eachで実行した場合(約81秒)(allよりも時間が掛かっている)<br> | ||
+ | [[画像:Outdata-PIONE-Advanced4-8.png|1390px]]<br> | ||
+ | <br> | ||
+ | |||
+ | eachを-t 2で実行した場合(約47秒)(速度が改善されている)<br> | ||
+ | [[画像:Outdata-PIONE-Advanced4-9.png|820px]]<br> | ||
+ | <br> | ||
+ | |||
+ | eachを-t 3で実行した場合(約36秒)(さらに速度が改善されている)<br> | ||
+ | [[画像:Outdata-PIONE-Advanced4-10.png|635px]]<br> | ||
+ | <br> | ||
+ | |||
+ | eachを-t 4で実行した場合(約31秒)(若干速度が改善されている)<br> | ||
+ | [[画像:Outdata-PIONE-Advanced4-11.png|550px]]<br> | ||
+ | <br> | ||
+ | |||
+ | 上記は1つのマシンにおいてallと複数のタスクによるeachの速度の違いです。eachを複数のタスクで動作させると、allの場合よりも速く処理できることが分かります。そして、タスク数が多いほどが速度が改善されています。しかし、タスク数を増やす毎に改善の度合いは緩やかになっています。マシン毎で一度に処理できるタスク数を把握し、全てのタスクをいくつに分けるかを考えると良いでしょう。<br> | ||
+ | <br> | ||
+ | |||
+ | Param.pione<br> | ||
+ | <pre> | ||
+ | # basic param | ||
+ | ## For Size | ||
+ | param $X_SIZE := 80 | ||
+ | param $Y_SIZE := 80 | ||
+ | param $SHRINK := 8 | ||
+ | param $SHRINK4S := 4 | ||
+ | |||
+ | ## For AutoRotationCorrelation | ||
+ | param $N_ROT := 72 | ||
+ | param $N_ROT1 := 72 | ||
+ | param $N_ROT2 := 72 | ||
+ | param $N_ROT3 := 1 | ||
+ | |||
+ | ## For RefData | ||
+ | param $REF_ROT_MODE := "XOYS" | ||
+ | param $REF_ROT1_START := 0 | ||
+ | param $REF_ROT1_END := 359 | ||
+ | param $REF_ROT1_DELTA := 15 | ||
+ | param $REF_ROT2_START := 0 | ||
+ | param $REF_ROT2_END := 359 | ||
+ | param $REF_ROT2_DELTA := 15 | ||
+ | param $REF_ROT3_START := 0 | ||
+ | param $REF_ROT3_END := 0 | ||
+ | param $REF_ROT3_DELTA := 15 | ||
+ | |||
+ | ## For SampleROI | ||
+ | param $ROI_ROT_MODE := "XOYS" | ||
+ | param $ROI_ROT1_START := 0 | ||
+ | param $ROI_ROT1_END := 359 | ||
+ | param $ROI_ROT1_DELTA := 30 | ||
+ | $roi_rot1_loop := ($ROI_ROT1_END - $ROI_ROT1_START) / $ROI_ROT1_DELTA | ||
+ | param $ROI_ROT2_START := 0 | ||
+ | param $ROI_ROT2_END := 359 | ||
+ | param $ROI_ROT2_DELTA := 30 | ||
+ | $roi_rot2_loop := ($ROI_ROT2_END - $ROI_ROT2_START) / $ROI_ROT2_DELTA | ||
+ | param $ROI_ROT3_START := 0 | ||
+ | param $ROI_ROT3_END := 0 | ||
+ | param $ROI_ROT3_DELTA := 30 | ||
+ | $roi_rot3_loop := ($ROI_ROT3_END - $ROI_ROT3_START) / $ROI_ROT3_DELTA | ||
+ | param $ROI_SN := 1 | ||
+ | |||
+ | # advanced param | ||
+ | ## Mode 0: do not, 1: each, 2: all | ||
+ | advanced param $Mode_ROI := 1 | ||
+ | advanced param $Mode_Ref := 1 | ||
+ | advanced param $Mode_Cor := 1 | ||
+ | ## Flag true: do, false: do not | ||
+ | advanced param $Flag_3D := true | ||
+ | advanced param $Flag_3DWhole := true | ||
+ | advanced param $Flag_3D4S := true | ||
+ | advanced param $Flag_Projection := true | ||
+ | </pre> | ||
+ | 今回はMode_ROIとMode_Corのみ1: each, 2: allの切り替えが可能としています。$roi_rot1_loopなどの[[変数束縛]]を追加していますが、これはeachのループ数のためでユーザが入力する必要はありません。<br> | ||
+ | <br> | ||
+ | |||
+ | これらのファイルは[[Media:PIONE-Advanced4-2.zip|こちら]]からダウンロードできます。<br> | ||
+ | <br> | ||
+ | |||
+ | ===== 同じようなルールをまとめる ===== | ||
+ | ====== Preprocess ====== | ||
+ | Mainで呼んでいるPreprocessのルールは下記のようにしていました。<br> | ||
+ | <table border="1"> | ||
+ | <tr> | ||
+ | <th>Main.pione内のルール</th> | ||
+ | <th>サブルール</th> | ||
+ | <th>Makefile</th> | ||
+ | <th>説明</th> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>Preprocess</td> | ||
+ | <td>pad_padlst<br> | ||
+ | roi_smooth<br> | ||
+ | smooth_shrink<br> | ||
+ | shrink_pad<br> | ||
+ | </td> | ||
+ | <td>.lst.padlst<br> | ||
+ | .roi.smooth<br> | ||
+ | .smooth.shrink<br> | ||
+ | .shrink.pad<br> | ||
+ | </td> | ||
+ | <td>.padファイル一覧を作成<br> | ||
+ | .roiの平滑化して.smoothを作成<br> | ||
+ | .smoothを縮小して.shrinkを作成<br> | ||
+ | .shrinkを参照画像と同じサイズにパディングして.padを作成<br> | ||
+ | </td> | ||
+ | </tr> | ||
+ | </table> | ||
+ | <br> | ||
+ | |||
+ | これらの処理は全て入力ファイルと出力ファイルが1:1対応であり、.roi -> .smooth -> .shrinkの順番に作成して最終的に.padファイルのみを得ることを目的としています。このような場合はルールを分けずに1つのルールの中で処理を行った方がルール間のファイルのやり取りが最小限で済みます。(参照:[[#基本5の補足]])<br> | ||
+ | <br> | ||
+ | |||
+ | <table border="1"> | ||
+ | <tr> | ||
+ | <th></th> | ||
+ | <th>コード</th> | ||
+ | <th>処理結果([[ProM]])</th> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>(1) 分けた場合</td> | ||
+ | <td> | ||
+ | <pre> | ||
+ | Rule Preprocess | ||
+ | input '*.roi' | ||
+ | output '{$I[1][1]}.pad' | ||
+ | Flow | ||
+ | rule roi_smooth; | ||
+ | rule smooth_shrink; | ||
+ | rule shrink_pad; | ||
+ | End | ||
+ | |||
+ | #.roi.smooth: | ||
+ | Rule roi_smooth | ||
+ | input '*.roi' | ||
+ | output '{$I[1][1]}.smooth' | ||
+ | Action | ||
+ | mrcImageSmoothing -i {$I[1]} -o {$O[1]} -m 1 -r 4 | ||
+ | End | ||
+ | |||
+ | #.smooth.shrink: | ||
+ | Rule smooth_shrink | ||
+ | input '*.smooth' | ||
+ | output '{$I[1][1]}.shrink' | ||
+ | Action | ||
+ | mrcImageShrink -i {$I[1]} -o {$O[1]} -S {$SHRINK} | ||
+ | End | ||
+ | |||
+ | #.shrink.pad: | ||
+ | Rule shrink_pad | ||
+ | input '*.shrink' | ||
+ | output '{$I[1][1]}.pad' | ||
+ | Action | ||
+ | width=`expr {$X_SIZE} / {$SHRINK}` | ||
+ | height=`expr {$Y_SIZE} / {$SHRINK}` | ||
+ | mrcImagePad -i {$I[1]} -o {$O[1]} -W ${width} -H ${height} | ||
+ | End | ||
+ | </pre> | ||
+ | </td> | ||
+ | <td>[[画像:Outdata-PIONE-Advanced4-5.png]]</td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>(2) まとめた場合</td> | ||
+ | <td> | ||
+ | <pre> | ||
+ | Rule Preprocess | ||
+ | input '*.roi' | ||
+ | output '{$I[1][1]}.pad' | ||
+ | Action | ||
+ | # roi_smooth | ||
+ | mrcImageSmoothing -i {$I[1]} -o {$I[1][1]}.smooth -m 1 -r 4 | ||
+ | |||
+ | # smooth_shrink | ||
+ | mrcImageShrink -i {$I[1][1]}.smooth -o {$I[1][1]}.shrink -S {$SHRINK} | ||
+ | |||
+ | # shrink_pad | ||
+ | width=`expr {$X_SIZE} / {$SHRINK}` | ||
+ | height=`expr {$Y_SIZE} / {$SHRINK}` | ||
+ | mrcImagePad -i {$I[1][1]}.shrink -o {$O[1]} -W ${width} -H ${height} | ||
+ | End | ||
+ | </pre> | ||
+ | </td> | ||
+ | <td>[[画像:Outdata-PIONE-Advanced4-6.png|540px]]</td> | ||
+ | </tr> | ||
+ | </table> | ||
+ | <br> | ||
+ | |||
+ | 上記の(2)のようにルールをまとめることで(1)に比べて、1ファイル毎に1秒ほど時間が短縮できました。<br> | ||
+ | <br> | ||
+ | |||
+ | ====== Create_fit, Create_3dlst, Create_3d ====== | ||
+ | Create_fitは各.padファイルから.fitファイルや.corinfoファイルをそれぞれ1:1で作成しています。また、Create_3dlstでもfit_3dinfoまでは1:1対応の処理です。<br> | ||
+ | |||
+ | <table border="1"> | ||
+ | <tr> | ||
+ | <th>Main.pione内のルール</th> | ||
+ | <th>サブルール</th> | ||
+ | <th>Makefile</th> | ||
+ | <th>説明</th> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>Create_fit</td> | ||
+ | <td>-</td> | ||
+ | <td>.pad.fit</td> | ||
+ | <td>.padから.stackを使って相関マップ.corinfoを作成(最も近い画像.fitも作成)</td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>Create_3dlst</td> | ||
+ | <td>fit_3dinfo<br> | ||
+ | Sum_3dinfo_3dlst</td> | ||
+ | <td>.fit.3dinfo</td> | ||
+ | <td>各相関マップ.corinfoから相関値が最大の角度を集めてリスト.3dlstを作成</td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>Create_3d</td> | ||
+ | <td>Reconstruct_3dlst_3d<br> | ||
+ | Convert_3d_ds6</td> | ||
+ | <td>.3dlst.3d<br> | ||
+ | .3d.ds6</td> | ||
+ | <td>リスト.3dlstと.fitを使って3次元像.3d及び.ds6を作成(1/8サイズ)</td> | ||
+ | </tr> | ||
+ | </table> | ||
+ | <br> | ||
+ | |||
+ | 従って、この部分もまとめた方が良いでしょう。同様にfit_3dwholeinfoとfit_3d4sinfoもまとめます。この処理は前項目のPreprocessともまとめられそうですが、[[単粒子解析]]のための3次元再構成では角度決定処理を繰り返し行うので、この部分は分けて実装します。(参照:[[単粒子解析#繰り返し(精密化)]])また、Create_3dlstのSum_3dinfo_3dlstとCreate_3dなども一つにまとめられます。<br> | ||
+ | <br> | ||
+ | |||
+ | まとめたCreate_fit_each<br> | ||
+ | <pre> | ||
+ | Rule Create_fit_each | ||
+ | input '*.pad' | ||
+ | input '*.stack' | ||
+ | if ($Flag_3DWhole or $Flag_3D4S) | ||
+ | input '{$I[1][1]}.roi' | ||
+ | end | ||
+ | if $Flag_3D | ||
+ | output '{$I[1][1]}.fit' | ||
+ | output '{$I[1][1]}.3dinfo' | ||
+ | end | ||
+ | if $Flag_3DWhole | ||
+ | output '{$I[1][1]}.shift' | ||
+ | output '{$I[1][1]}.3dwholeinfo' | ||
+ | end | ||
+ | if $Flag_3D4S | ||
+ | output '{$I[1][1]}.4shift' | ||
+ | output '{$I[1][1]}.3d4sinfo' | ||
+ | end | ||
+ | Action | ||
+ | # Create_fit | ||
+ | mrcImageAutoRotationCorrelation -i {$I[1]} -r {$I[2]} -fit {$I[1][1]}.fit -O {$I[1][1]}.corinfo \ | ||
+ | -n {$N_ROT} -m 18 -nRot1 {$N_ROT1} -nRot2 {$N_ROT2} -nRot3 {$N_ROT3} > /dev/null | ||
+ | |||
+ | # fit_3dinfo | ||
+ | if {$Flag_3D} ; then | ||
+ | awk '/Cor/ { print $18,$16,$2,$3,$4,"0.0"}' {$I[1][1]}.corinfo | sort -r | sed -e s/pad/fit/ > {$I[1][1]}.3dinfolst | ||
+ | head -n 1 {$I[1][1]}.3dinfolst | awk ' {print $2,$3,$4,$5,$6,$1'} > {$I[1][1]}.3dinfo | ||
+ | fi | ||
+ | |||
+ | # fit_3dwholeinfo | ||
+ | if {$Flag_3DWhole} ; then | ||
+ | awk '/Cor/ { print $18,$16,$2,$3,$4,$9,$11,$12}' {$I[1][1]}.corinfo | sort -r | sed -e s/pad/shift/ > {$I[1][1]}.3dwholeinfolst | ||
+ | head -n 1 {$I[1][1]}.3dwholeinfolst | awk ' {print $2,$3,$4,$5,$6,$7,$8,$1'} > {$I[1][1]}.3dwholeinfo | ||
+ | X=`awk '{print -{$SHRINK}*$6; }' {$I[1][1]}.3dwholeinfo`; | ||
+ | Y=`awk '{print -{$SHRINK}*$7; }' {$I[1][1]}.3dwholeinfo`; | ||
+ | echo $X,$Y; mrcImageShift -i {$I[1][1]}.roi -o {$I[1][1]}.shift -x $X -y $Y -z 0 | ||
+ | fi | ||
+ | |||
+ | # fit_3d4sinfo | ||
+ | if {$Flag_3D4S} ; then | ||
+ | awk '/Cor/ { print $18,$16,$2,$3,$4,$9,$11,$12}' {$I[1][1]}.corinfo | sort -r | sed -e s/pad/4shift/ > {$I[1][1]}.3d4sinfolst | ||
+ | head -n 1 {$I[1][1]}.3d4sinfolst | awk ' {print $2,$3,$4,$5,$6,$7,$8,$1'} > {$I[1][1]}.3d4sinfo | ||
+ | X=`awk '{print -{$SHRINK4S}*$6; }' {$I[1][1]}.3d4sinfo`; | ||
+ | Y=`awk '{print -{$SHRINK4S}*$7; }' {$I[1][1]}.3d4sinfo`; | ||
+ | echo $X,$Y; | ||
+ | mrcImageShrink -i {$I[1][1]}.roi -o {$I[1][1]}.4shrink -S {$SHRINK4S} | ||
+ | width=`expr {$X_SIZE} / {$SHRINK4S}` | ||
+ | height=`expr {$Y_SIZE} / {$SHRINK4S}` | ||
+ | mrcImagePad -i {$I[1][1]}.4shrink -o {$I[1][1]}.4spad -W ${width} -H ${height} | ||
+ | mrcImageShift -i {$I[1][1]}.4spad -o {$I[1][1]}.4shift -x $X -y $Y -z 0 | ||
+ | fi | ||
+ | End | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | まとめたCreate_3d | ||
+ | <pre> | ||
+ | Rule Create_3d | ||
+ | input '*.3dinfo'.all | ||
+ | input '*.fit'.all | ||
+ | output '{$filename}.3d' | ||
+ | output '{$filename}.ds6' | ||
+ | param $filename | ||
+ | Action | ||
+ | # Sum_3dinfo_3dlst | ||
+ | for info in {$I[1]} | ||
+ | do | ||
+ | cat $info >> {$filename}.3dlst | ||
+ | done | ||
+ | |||
+ | # Reconstruct_3dlst_3d | ||
+ | mrc2Dto3D -I {$filename}.3dlst -o {$O[1]} -InterpolationMode 2 -Double -DoubleCounter {$filename}.3dcounter -CounterThreshold 0.5 -m 1 -WeightMode 6 | ||
+ | |||
+ | # Convert_3d_ds6 | ||
+ | mrc2map -i {$O[1]} -o {$O[2]} -m 3 | ||
+ | End | ||
+ | </pre> | ||
+ | Create_3d4s, Create_3dwholeについても同様にまとめています。<br> | ||
+ | <br> | ||
+ | |||
+ | ===== 動作比較 ===== | ||
+ | 作成した[[PIONE定義書]]と前回分や元の[[Makefile]]との処理時間を比較して、ここまでの実装の効果があったかを検証します。今回は.3dファイルの作成に関して比較します。<br> | ||
+ | <br> | ||
+ | |||
+ | では、[[:Media:PIONE-Advanced4-3.zip|今回の実装]]による処理時間を複数のタスク(-t 2と-t 4)で実行してみます。<br> | ||
+ | <br> | ||
+ | |||
+ | -t 2の結果(5分強)<br> | ||
+ | [[画像:Outdata-PIONE-Advanced4-14.png]]<br> | ||
+ | <br> | ||
+ | |||
+ | -t4の結果(3分強)<br> | ||
+ | [[画像:Outdata-PIONE-Advanced4-15.png]]<br> | ||
+ | <br> | ||
+ | |||
+ | 1つのマシンにおいて2つのタスクに分けることで4分削減して約5/9(改善前からは2分削減して約5/7)になりました。さらに4つのタスクに分けることで6分削減して約1/3になりました。入力ファイル(256×256:36枚)の場合において、今回の実装で有効な並列処理ができたと言えるでしょう。<br> | ||
+ | <br> | ||
+ | |||
+ | ==== 機能追加のための実装 ==== | ||
+ | 今回は作成した[[PIONE定義書]]や[[パッケージ]]についてさらに機能を追加する方法を考えます。<br> | ||
+ | <br> | ||
+ | |||
+ | ===== 粒子の切り出しを追加する ===== | ||
+ | .roiファイルが無い場合にユーザが切り出した画像を使用できるように追加してみましょう。(参照:[[単粒子解析#電子顕微鏡画像から粒子画像の抽出]])<br> | ||
+ | <br> | ||
+ | |||
+ | この機能は.roiファイルを作成するものなので、Create_ROIと同列と考えて、$Mode_ROIに3を追加してこのときにユーザが切り出すようにします。<br> | ||
+ | <br> | ||
+ | |||
+ | Main.pione(Cutout_ROIを追加)<br> | ||
+ | <pre> | ||
+ | case $Mode_ROI | ||
+ | when 1 | ||
+ | rule Create_Sample3d | ||
+ | rule Create_SampleROI_each {rot1: (0.upto($roi_rot1_loop))*$ROI_ROT1_DELTA, rot2: (0.upto($roi_rot2_loop))*$ROI_ROT2_DELTA, rot3: (0.upto($roi_rot3_loop))*$ROI_ROT3_DELTA} | ||
+ | when 2 | ||
+ | rule Create_Sample3d | ||
+ | rule Create_SampleROI_all | ||
+ | when 3 | ||
+ | rule Cutout_ROI | ||
+ | end | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | Parameter.pione(コメントを追加)<br> | ||
+ | <pre> | ||
+ | ## Mode 0: do not, 1: each, 2: all, 3: use Display2 | ||
+ | advanced param $Mode_ROI := 3 | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | Create_SampleROI.pione(Cutout_ROIを追加)<br> | ||
+ | <pre> | ||
+ | Rule Cutout_ROI | ||
+ | input '*.mrc' | ||
+ | output '*.roi'.all | ||
+ | output '*.tiff'.all | ||
+ | Action | ||
+ | Display2 -i {$I[1]} | ||
+ | for data in $(ls *.roi) | ||
+ | do | ||
+ | mrc2tiff -i ${data} -o ${data}.tiff | ||
+ | done | ||
+ | End | ||
+ | </pre> | ||
+ | 切り出しには[[Display2]]を使用しています。<br> | ||
+ | <br> | ||
+ | |||
+ | 新しいルールを作成して、呼び出すだけで簡単に機能を追加することができました。<br> | ||
+ | <br> | ||
+ | |||
+ | ===== 繰り返し機能を追加する ===== | ||
+ | [[単粒子解析]]のための3次元再構成は出来上がった3次元像を参照像として繰り返して、3次元像の精密化を行うこともあります。今回はこの部分の対応を作成してみましょう。<br> | ||
+ | <br> | ||
+ | |||
+ | Parameter.pione(繰り返し回数を追加)<br> | ||
+ | <pre> | ||
+ | ## For repeat count | ||
+ | param $Repeat_Count := 2 | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | Repeat_3d<br> | ||
+ | <pre> | ||
+ | Rule Repeat_3d | ||
+ | input '*-{$count}.3d' | ||
+ | output '{$I[1][1]}-{$count + 1}.stack' | ||
+ | param $count | ||
+ | Action | ||
+ | mrc3Dto2D -i {$I[1]} -o {$O[1]} -m 1 -InterpolationMode 2 -EulerMode {$REF_ROT_MODE} \ | ||
+ | -Rot1 {$REF_ROT1_START} {$REF_ROT1_END} {$REF_ROT1_DELTA} \ | ||
+ | -Rot2 {$REF_ROT2_START} {$REF_ROT2_END} {$REF_ROT2_DELTA} \ | ||
+ | -Rot3 {$REF_ROT3_START} {$REF_ROT3_END} {$REF_ROT3_DELTA} | ||
+ | End | ||
+ | </pre> | ||
+ | 上記のように作成された.3dファイルから.stackファイルを作成します。このとき繰り返し回数が分かるようにファイル名にを付加しています。また、下記のルールについても繰り返し回数の対応を行います。<br> | ||
+ | <br> | ||
+ | |||
+ | Create_stack.pione<br> | ||
+ | <pre> | ||
+ | output '{$I[1][1]}-1.stack' | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | Main.pione(出力ファイルの宣言)<br> | ||
+ | <pre> | ||
+ | if $Flag_3D | ||
+ | output '{$I[1][1]}-{$Repeat_Count}.3d' | ||
+ | output '{$I[1][1]}-{$Repeat_Count}.ds6' | ||
+ | end | ||
+ | </pre> | ||
+ | 繰り返しの最後のファイルのみを出力するようにします。<br> | ||
+ | <br> | ||
+ | |||
+ | Main.pione(Flow)<br> | ||
+ | <pre> | ||
+ | case $Mode_3D | ||
+ | when 1 | ||
+ | rule Create_fit_each | ||
+ | |||
+ | if $Flag_3D | ||
+ | rule Create_3d {filename: $I[1][1].str(), count: 1.upto($Repeat_Count)} | ||
+ | rule Repeat_3d {count: 1.upto($Repeat_Count - 1)} | ||
+ | end | ||
+ | </pre> | ||
+ | Create_3d, Repeat_3dを繰り返し回数分呼び出します。<br> | ||
+ | <br> | ||
+ | |||
+ | Create_stack.pione(最初の.stackファイル名に1を付加) | ||
+ | <pre> | ||
+ | output '{$I[1][1]}-1.stack' | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | Create_fit_each.pione(.3dのための処理)<br> | ||
+ | <pre> | ||
+ | Rule Create_fit_each | ||
+ | input '*.pad' | ||
+ | input '*-*.stack' | ||
+ | if $Flag_3D | ||
+ | output '{$I[1][1]}.fit-{$I[2][2]}' | ||
+ | output '{$I[1][1]}.3dinfo-{$I[2][2]}' | ||
+ | end | ||
+ | Action | ||
+ | # Create_fit | ||
+ | mrcImageAutoRotationCorrelation -i {$I[1]} -r {$I[2]} -fit {$I[1][1]}.fit-{$I[2][2]} -O {$I[1][1]}.corinfo-{$I[2][2]} \ | ||
+ | -n {$N_ROT} -m 18 -nRot1 {$N_ROT1} -nRot2 {$N_ROT2} -nRot3 {$N_ROT3} > /dev/null | ||
+ | |||
+ | # fit_3dinfo | ||
+ | if {$Flag_3D} ; then | ||
+ | awk '/Cor/ { print $18,$16,$2,$3,$4,"0.0"}' {$I[1][1]}.corinfo-{$I[2][2]} | sort -r | sed -e s/pad/fit-{$I[2][2]}/ > {$I[1][1]}.3dinfolst-{$I[2][2]} | ||
+ | head -n 1 {$I[1][1]}.3dinfolst-{$I[2][2]} | awk ' {print $2,$3,$4,$5,$6,$1'} > {$I[1][1]}.3dinfo-{$I[2][2]} | ||
+ | fi | ||
+ | |||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | Create_3d.pione<br> | ||
+ | <pre> | ||
+ | Rule Create_3d | ||
+ | input '*.3dinfo-{$count}'.all | ||
+ | input '*.fit-{$count}'.all | ||
+ | output '{$filename}-{$count}.3d' | ||
+ | output '{$filename}-{$count}.ds6' | ||
+ | param $filename | ||
+ | param $count | ||
+ | Action | ||
+ | # Sum_3dinfo_3dlst | ||
+ | for info in {$I[1]} | ||
+ | do | ||
+ | cat $info >> {$filename}-{$count}.3dlst | ||
+ | done | ||
+ | |||
+ | # Reconstruct_3dlst_3d | ||
+ | mrc2Dto3D -I {$filename}-{$count}.3dlst -o {$O[1]} -InterpolationMode 2 -Double -DoubleCounter {$filename}-{$count}.3dcounter -CounterThreshold 0.5 -m 1 -WeightMode 6 | ||
+ | |||
+ | # Convert_3d_ds6 | ||
+ | mrc2map -i {$O[1]} -o {$O[2]} -m 3 | ||
+ | End | ||
+ | </pre> | ||
+ | |||
+ | このように実装することで指定した数Repeat_Countに応じた繰り返し処理を行うように機能追加ができます。<br> | ||
+ | <br> | ||
+ | |||
+ | === 応用5(クラスター解析) === | ||
+ | 次は[[クラスター解析]]のMakefileを元にして速度改善を考えてみましょう。[http://sourceforge.jp/projects/eos/scm/git/base/archive/master/Integration/2DClustering/?format=zip こちらのMakefile]を元にしています。今回はMakefileのコマンドも使用します。これにより全体の実装量を削減できますが、makeコマンド実行するときに入出力ファイルがそれぞれ何に当たるのかを注意する必要があります。<br> | ||
+ | <br> | ||
+ | |||
+ | ==== 前処理 ==== | ||
+ | 前処理の並列化を考えます。これまでの[[PIONE定義書]]ではeachを使って1ファイル毎の並列処理として実装を行いました。今回はいくつかのグループに分けて、そのグループ毎で並列化を行うようにしてみます。<br> | ||
+ | <br> | ||
+ | |||
+ | <pre> | ||
+ | param $task := 2 | ||
+ | |||
+ | Rule Main | ||
+ | input '*.roi'.all | ||
+ | input 'Makefile' | ||
+ | input 'Makefile.config' | ||
+ | output '*.pad'.all | ||
+ | Flow | ||
+ | rule Preprocess {filelist:$I[1].all, num:1.upto($task), length:$I[1].length()} | ||
+ | End | ||
+ | |||
+ | Rule Preprocess | ||
+ | input $filelist.nth(((($num-1)*((($length-1)/$task)+1))+1).upto((($num*((($length-1)/$task)+1))|$length).min())) | ||
+ | input 'Makefile' | ||
+ | input 'Makefile.config' | ||
+ | output '*.pad'.all | ||
+ | Action | ||
+ | for data in {$I[1]} | ||
+ | do | ||
+ | make $(basename ${data} ".roi").pad | ||
+ | done | ||
+ | End | ||
+ | </pre> | ||
+ | Preprocess内の処理はMakefile内のコマンドに委ねるようにしています。入力ファイルがかなり複雑となっていますが、これは次のように考えています。<br> | ||
+ | <br> | ||
+ | |||
+ | まずパラメータtaskで分割したいグループ数を決め、この数だけルールPreprocessの呼び出すように考えます。<br> | ||
+ | *filelistにはファイル自体でなく、ファイル名の一覧を送る | ||
+ | *numには分割数の内の何番目かを送る | ||
+ | *lengthにはファイル名の要素数を送る | ||
+ | 次にPreprocess側では受け取ったパラメータからグループ毎で使用するファイル名を決め、それらを入力ファイルとすることで分割を実現します。なお、今回使用しているupto, length, minなどのメソッドについては[[PIONEの式]]を参照して下さい。<br> | ||
+ | <br> | ||
+ | |||
+ | 次のグラフは上記の前処理を256, 512, 1024, 2048, 4096(pixel)の画像ファイル100枚を入力としてタスク数1, 2, 4, 10で動作したときの処理時間です。<br> | ||
+ | [[画像:Outdata-PIONE-Advanced5-1.png]]<br> | ||
+ | allは(前処理に関する)全体の処理、processは前処理自体、otherはそれ以外のファイル通信などの処理時間を表しています。<br> | ||
+ | <br> | ||
+ | |||
+ | ここでタスク数を増やしたときの変化に注目します。全体の処理時間についてt=1(シングルタスク)との比率は下記のようになりました。<br> | ||
+ | [[画像:Outdata-PIONE-Advanced5-2.png]]<br> | ||
+ | 2048サイズまでは処理時間が短くなっています。今回はタスク数を増やしても速度の改善は見られません。タスク数を増やすことによって前処理自体の時間は軽減されますが、それ以外のファイル通信など処理で逆に時間がかかってしまうためです。<br> | ||
+ | <br> | ||
+ | |||
+ | <table> | ||
+ | <tr> | ||
+ | <td>[[画像:Outdata-PIONE-Advanced5-5.png]]</td> | ||
+ | <td>[[画像:Outdata-PIONE-Advanced5-6.png]]</td> | ||
+ | </tr> | ||
+ | <tr> | ||
+ | <td>前処理はタスク数が多い方が速くなる(このマシンではt=4まで)</td> | ||
+ | <td>タスク数が多くなると並列処理の準備(ファイル通信など)は時間がかかる。</td> | ||
+ | </tr> | ||
+ | <table> | ||
+ | <br> | ||
+ | |||
+ | ==== クラスター解析による分類 ==== | ||
+ | 本処理のクラスター解析による分類の速度改善を考えてみましょう。Makefileでは[[mrcImageClusterAnalysis]]によって画像毎の相関リストを作って近い画像同士を分類して、グループを作り、それぞれのグループによって平均画像を作成しています。今回はこのグループ毎の平均画像に対してリファインメントを行う部分にて並列化を行ってみます。<br> | ||
+ | <br> | ||
+ | |||
+ | ===== グループ分け ===== | ||
+ | まず、全ての.padファイルを入力としてmakeコマンドによってクラスター解析による分類を行います。すると、分類された.padファイルの一覧all.padsortlstと樹形図all.treeinfoが出力されるので、この2つのファイルを指定した分割数divideだけ分割してそれぞれ.lst .treeファイルを作成します。また、最初のmakeコマンドによって1回目の平均画像が得られますので、この画像がどのグループに属するのか番号付けして出力しています。(ルートに近く分割できない画像は0として番号付けする)下記はその実装例です。<br> | ||
+ | <br> | ||
+ | |||
+ | <pre> | ||
+ | Rule Process | ||
+ | input '*.pad'.all | ||
+ | input 'Makefile' | ||
+ | input 'Makefile.config' | ||
+ | output 'all.padsortlst' | ||
+ | output 'all.treeinfo' | ||
+ | output ((1.upto($divide).str())+".lst").d().all() | ||
+ | output ((1.upto($divide).str())+".tree").d().all() | ||
+ | output ("*.pad"+(1.upto($divide).str())).d().all() | ||
+ | output ("*.avg"+(0.upto($divide).str())).d().all() | ||
+ | output '*.avglst'.all | ||
+ | Action | ||
+ | ls -1 *.pad > all.padlst | ||
+ | make Log | ||
+ | make LogPS | ||
+ | |||
+ | cp {$O[1]} 1.lst | ||
+ | cp {$O[2]} 1.tree | ||
+ | for (( i=2; i<={$divide}; i++ )) | ||
+ | do | ||
+ | max=0 | ||
+ | for data in $(ls *.lst) | ||
+ | do | ||
+ | num=$(wc -l ${data} | awk '{printf $1}') | ||
+ | if [ ${num} -gt ${max} ] ; then | ||
+ | max=${num} | ||
+ | maxlist="${data}" | ||
+ | fi | ||
+ | done | ||
+ | maxtree="$(basename ${maxlist} '.lst').tree" | ||
+ | root=$(head -1 ${maxtree} | awk '{printf("%d", $1)}') | ||
+ | rootname=$(basename $(awk -v val=${root} '$2==val {printf("%s", $1)}' ${maxlist}) ".pad") | ||
+ | cp ${maxlist} ${rootname}.avglst | ||
+ | cp ${rootname}.pad.avg ${rootname}.pad.avg0 | ||
+ | maxpos=$(awk -v val=${root} '$2==val {printf("%f", $3)}' ${maxlist}) | ||
+ | line=$(awk -v val=${root} '$2==val {print NR}' ${maxlist}) | ||
+ | head -$((${line} - 1)) ${maxlist} > ${i}.lst | ||
+ | tail -$(($(wc -l ${maxlist} | awk '{print $1}') - ${line} + 1)) ${maxlist} > tmp | ||
+ | cp tmp ${maxlist} | ||
+ | |||
+ | max_t=$(wc -l ${maxtree} | awk '{printf $1}') | ||
+ | max_l=$(wc -l ${maxlist} | awk '{printf $1}') | ||
+ | line_t=$(awk -v val=${root} '$1==val {print NR}' ${maxtree}) | ||
+ | head -$((${max_l} + ${line_t} - 1)) ${maxtree} | tail -$((${max_l} - 1)) > tmp | ||
+ | tail -$((${max_t} - ${max_l})) ${maxtree} > ${i}.tree | ||
+ | cp tmp ${maxtree} | ||
+ | done | ||
+ | |||
+ | for (( i=1; i<={$divide}; i++ )) | ||
+ | do | ||
+ | for data in $(awk '{print $1}' ${i}.lst) | ||
+ | do | ||
+ | cp ${data} ${data}${i} | ||
+ | cp ${data}.avg ${data}.avg${i} | ||
+ | done | ||
+ | done | ||
+ | End | ||
+ | </pre> | ||
+ | .lst .treeファイルが最大のものから2分割するようにし、分割する前の.lstファイルを.avglstとしてグループ0の平均画像のデータとして取り扱うようにしています。<br> | ||
+ | <br> | ||
+ | |||
+ | ===== グループ毎の平均画像のリファインメント ===== | ||
+ | グループ分けされた平均画像についてのリファインメントを行います。まず、.lst .treeファイルから平均画像を構成している画像リスト.avglstファイルを作成します。そして、そのリストを使用してリファインメントを行います。このとき、[[mrcImageAutoRotationCorrelation]]と[[mrcImageAverage]]を使用していますが、設定内容をMakefileと同じにするためにMakefile.configから特定のパラメータを読み取るようにしています。<br> | ||
+ | <br> | ||
+ | |||
+ | <pre> | ||
+ | Rule Refinement | ||
+ | input '*.pad{$num}'.all | ||
+ | input '*.avg{$num}'.all | ||
+ | input '{$num}.lst' | ||
+ | input '{$num}.tree' | ||
+ | input 'Makefile.config' | ||
+ | output ($I[1][1].str()+".pad.avg").d().all() | ||
+ | output ($I[1][1].str()+".pad.avg.tiff").d().all() | ||
+ | output ($I[1][1].str()+".pad.avg"+$num.str()+".tiff").d().all() | ||
+ | Action | ||
+ | max=$(wc -l {$num}.tree | awk '{printf $1}') | ||
+ | data=$(head -1 {$num}.tree) | ||
+ | i=$(echo ${data} | awk '{print $1}') | ||
+ | name=$(basename $(awk -v i=${i} '$2==i {print $1}' {$num}.lst) ".pad") | ||
+ | cp {$num}.lst ${name}.avglst | ||
+ | cp {$num}.tree ${name}.tree | ||
+ | |||
+ | for (( k=1; k<=max ; k++ )) | ||
+ | do | ||
+ | i=$(head -${k} {$num}.tree | tail -1 | awk '{print $1}') | ||
+ | name=$(basename $(awk -v i=${i} '$2==i {print $1}' {$num}.lst) ".pad") | ||
+ | |||
+ | if [ $(wc -l ${name}.avglst | awk '{print $1}') -gt 2 ] ; then | ||
+ | tpos=$(awk -v i=${i} '$2==i {print NR}' ${name}.avglst) | ||
+ | tline=$(wc -l ${name}.avglst | awk '{print $1}') | ||
+ | |||
+ | head -$((${tline} - ${tpos} + 1)) ${name}.tree | tail -$((${tline} - ${tpos})) > tmp | ||
+ | i_sub1=$(head -1 tmp | tail -1 | awk '{print $1}') | ||
+ | name_sub1=$(basename $(awk -v i=${i_sub1} '$2==i {print $1}' ${name}.avglst) ".pad") | ||
+ | if [ $(wc -l tmp | awk '{print $1}') -ne 0 ] ; then | ||
+ | cp tmp ${name_sub1}.tree | ||
+ | tail -$((${tline} - ${tpos} + 1)) ${name}.avglst > ${name_sub1}.avglst | ||
+ | fi | ||
+ | |||
+ | tail -$((${tpos} - 2)) ${name}.tree > tmp | ||
+ | i_sub2=$(head -1 tmp | awk '{print $1}') | ||
+ | name_sub2=$(basename $(awk -v i=${i_sub2} '$2==i {print $1}' ${name}.avglst) ".pad") | ||
+ | if [ $(wc -l tmp | awk '{print $1}') -ne 0 ] ; then | ||
+ | cp tmp ${name_sub2}.tree | ||
+ | head -$((${tpos} - 1)) ${name}.avglst > ${name_sub2}.avglst | ||
+ | fi | ||
+ | fi | ||
+ | done | ||
+ | |||
+ | cat Makefile.config | sed -e s/'='/' '/ > Makefile.config.tmp | ||
+ | ClusterCorrelationMode=$(awk '$1=="ClusterCorrelationMode" {print $2}' Makefile.config.tmp) | ||
+ | ClusterRotationRangeMin=$(awk '$1=="ClusterRotationRangeMin" {print $2}' Makefile.config.tmp) | ||
+ | ClusterRotationRangeMax=$(awk '$1=="ClusterRotationRangeMax" {print $2}' Makefile.config.tmp) | ||
+ | ClusterRotationRangePartitionNumber=$(awk '$1=="ClusterRotationRangePartitionNumber" {print $2}' Makefile.config.tmp) | ||
+ | ClusterRotationIterationNumber=$(awk '$1=="ClusterRotationIterationNumber" {print $2}' Makefile.config.tmp) | ||
+ | |||
+ | for data in $(ls *.avglst) | ||
+ | do | ||
+ | name=$(basename ${data} ".avglst") | ||
+ | awk '{print $1}' ${data} | sed -e s/.pad/.pad.fit/ > ${name}.fitlst | ||
+ | cp ${name}.pad.avg{$num} ${name}.pad.avg | ||
+ | for (( i=0; i < {$refine}; i++ )) | ||
+ | do | ||
+ | for element in $(awk '{print $1}' ${data}) | ||
+ | do | ||
+ | mrcImageAutoRotationCorrelation -i ${element}{$num} -r ${name}.pad.avg -fit ${element}.fit -cor ${element}.cor \ | ||
+ | -Method ${ClusterRotationCorrelationMode} -m ${ClusterCorrelationMode} -Iter ${ClusterRotationIterationNumber} \ | ||
+ | -range ${ClusterRotationRangeMin} ${ClusterRotationRangeMax} -n ${ClusterRotationRangePartitionNumber} | ||
+ | done | ||
+ | |||
+ | mrcImageAverage -i ${name}.fitlst -o ${name}.pad.avg | ||
+ | done | ||
+ | mrc2tiff -i ${name}.pad.avg -o ${name}.pad.avg.tiff | ||
+ | mrc2tiff -i ${name}.pad.avg{$num} -o ${name}.pad.avg{$num}.tiff | ||
+ | done | ||
+ | End | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | ===== ルートに近い平均画像のリファインメント ===== | ||
+ | ルートに近く、分割できない平均画像についてもリファインメントを行います。ルールRefinementとほとんど同じですが、平均画像を構成している画像リストはすでにルールProcessで作成されているので、本処理のみで十分です。<br> | ||
+ | <br> | ||
+ | |||
+ | <pre> | ||
+ | Rule RefinementRoot | ||
+ | input '*.pad'.all | ||
+ | input '*.pad.avg0'.all | ||
+ | input ($I[2][1].str()+".avglst").d().all() | ||
+ | input 'Makefile.config' | ||
+ | output ($I[1][1].str()+".pad.avg").d().all() | ||
+ | output ($I[1][1].str()+".pad.avg.tiff").d().all() | ||
+ | output ($I[1][1].str()+".pad.avg0.tiff").d().all() | ||
+ | Action | ||
+ | cat Makefile.config | sed -e s/'='/' '/ > Makefile.config.tmp | ||
+ | ClusterCorrelationMode=$(awk '$1=="ClusterCorrelationMode" {print $2}' Makefile.config.tmp) | ||
+ | ClusterRotationRangeMin=$(awk '$1=="ClusterRotationRangeMin" {print $2}' Makefile.config.tmp) | ||
+ | ClusterRotationRangeMax=$(awk '$1=="ClusterRotationRangeMax" {print $2}' Makefile.config.tmp) | ||
+ | ClusterRotationRangePartitionNumber=$(awk '$1=="ClusterRotationRangePartitionNumber" {print $2}' Makefile.config.tmp) | ||
+ | ClusterRotationIterationNumber=$(awk '$1=="ClusterRotationIterationNumber" {print $2}' Makefile.config.tmp) | ||
+ | ClusterRotationCorrelationMode=$(awk '$1=="ClusterRotationCorrelationMode" {print $2}' Makefile.config.tmp) | ||
+ | |||
+ | for data in $(ls *.avglst) | ||
+ | do | ||
+ | name=$(basename ${data} ".avglst") | ||
+ | awk '{print $1}' ${data} | sed -e s/.pad/.pad.fit/ > ${name}.fitlst | ||
+ | cp ${name}.pad.avg0 ${name}.pad.avg | ||
+ | for (( i=0; i < {$refine}; i++ )) | ||
+ | do | ||
+ | for element in $(awk '{print $1}' ${data}) | ||
+ | do | ||
+ | mrcImageAutoRotationCorrelation -i ${element} -r ${name}.pad.avg -fit ${element}.fit -cor ${element}.cor \ | ||
+ | -Method ${ClusterRotationCorrelationMode} -m ${ClusterCorrelationMode} -Iter ${ClusterRotationIterationNumber} \ | ||
+ | -range ${ClusterRotationRangeMin} ${ClusterRotationRangeMax} -n ${ClusterRotationRangePartitionNumber} | ||
+ | done | ||
+ | |||
+ | mrcImageAverage -i ${name}.fitlst -o ${name}.pad.avg | ||
+ | done | ||
+ | mrc2tiff -i ${name}.pad.avg -o ${name}.pad.avg.tiff | ||
+ | mrc2tiff -i ${name}.pad.avg0 -o ${name}.pad.avg0.tiff | ||
+ | done | ||
+ | End | ||
+ | </pre> | ||
+ | <br> | ||
+ | |||
+ | ===== 動作検証 ===== | ||
+ | では実行してみましょう。<br> | ||
+ | 今回は入力画像を32×32(Pixel)を54枚用意しリファインメントを5回として、-t毎の実行時間を検証してみます。画像は全処理のログで、今回の実装部分はRefinement(ログの2分辺り)からです。(入力画像は同ディレクトリ内のMain_ROIr.pioneで作成しました)<br> | ||
+ | <br> | ||
+ | |||
+ | t=1(5分強)<br> | ||
+ | [[画像:Outdata-PIONE-Advanced5-t1.png]]<br> | ||
+ | <br> | ||
+ | |||
+ | t=2(4分程度)<br> | ||
+ | [[画像:Outdata-PIONE-Advanced5-t2.png|730px]]<br> | ||
+ | <br> | ||
+ | |||
+ | t=4(3分強)<br> | ||
+ | [[画像:Outdata-PIONE-Advanced5-t4.png|670px]]<br> | ||
+ | <br> | ||
+ | |||
+ | タスク数を増やす毎に処理時間の短縮(本処理が半分以下に軽減)が実現できています。今回の検証では入力画像の枚数が少ないので、ファイル通信があまりなく本処理の時間にほぼ直接影響されるため並列処理の効果が得られました。<br> | ||
+ | <br> | ||
+ | |||
+ | ==== Makefile内のパラメータ設定 ==== | ||
+ | 今回は処理の一部をMakefileの処理に委ねていますが、使用するパラメータはMakefile.configに設定するようになっています。パラメータ管理をまとめて行えるようにこの部分のパラメータもPIONEで設定できるようにしてみましょう。<br> | ||
+ | <br> | ||
+ | |||
+ | 元のMakefile.configは下記のようになっています。<br> | ||
+ | <pre> | ||
+ | # Makefile.config for 2D Clustering | ||
+ | |||
+ | # Cluster Name | ||
+ | CLUSTER=all | ||
+ | |||
+ | # Pad Parameter | ||
+ | # Width(Nx) before shrinking | ||
+ | PADWIDTH=32 | ||
+ | # Height(Ny) before shrinking | ||
+ | PADHEIGHT=32 | ||
+ | # Shrink for Speed Up in clustering | ||
+ | SHRINK=1 | ||
+ | # Pad Mode | ||
+ | PADMODE=13 | ||
+ | # LOWPASS | ||
+ | LowPassMode=4 | ||
+ | LowPassResolution=0.1 | ||
+ | |||
+ | # Clustering | ||
+ | ClusterCorrelationMode=19 | ||
+ | # | ||
+ | ClusterRotationRangeMin=0 | ||
+ | ClusterRotationRangeMax=360 | ||
+ | ClusterRotationRangePartitionNumber=72 | ||
+ | ClusterRotationIterationNumber=2 | ||
+ | ClusterRotationCorrelationMode=0 | ||
+ | # | ||
+ | ClusterMode=2 | ||
+ | |||
+ | |||
+ | # ClusterShow | ||
+ | TreeRootPositionX=0 | ||
+ | TreeRootPositionY=400 | ||
+ | TreeScaleX=10 | ||
+ | TreeScaleY=100 | ||
+ | TreeOffset=1e1 | ||
+ | </pre> | ||
+ | 今回はPIONEで設定したパラメータから上記のようなMakefile.configを作成することで各種パラメータの管理を全てPIONE定義書内で行えるようにします。<br> | ||
+ | <br> | ||
+ | |||
+ | Main.pione内に下記の内容を追加します。<br> | ||
+ | <pre> | ||
+ | # Cluster Name | ||
+ | param $CLUSTER := "all" | ||
+ | |||
+ | # Pad Parameter | ||
+ | # Width(Nx) before shrinking | ||
+ | param $PADWIDTH := 32 | ||
+ | # Height(Ny) before shrinking | ||
+ | param $PADHEIGHT := 32 | ||
+ | # Shrink for Speed Up in clustering | ||
+ | param $SHRINK := 1 | ||
+ | # Pad Mode | ||
+ | param $PADMODE := 13 | ||
+ | # LOWPASS | ||
+ | param $LowPassMode := 4 | ||
+ | param $LowPassResolution := 0.1 | ||
+ | |||
+ | # Clustering | ||
+ | param $ClusterCorrelationMode := 19 | ||
+ | # | ||
+ | param $ClusterRotationRangeMin := 0 | ||
+ | param $ClusterRotationRangeMax := 360 | ||
+ | param $ClusterRotationRangePartitionNumber := 72 | ||
+ | param $ClusterRotationIterationNumber := 2 | ||
+ | param $ClusterRotationCorrelationMode := 0 | ||
+ | # | ||
+ | param $ClusterMode := 2 | ||
+ | |||
+ | |||
+ | # ClusterShow | ||
+ | param $TreeRootPositionX := 0 | ||
+ | param $TreeRootPositionY := 400 | ||
+ | param $TreeScaleX := 10 | ||
+ | param $TreeScaleY := 100 | ||
+ | param $TreeOffset := "1e1" | ||
+ | |||
+ | # Sequence with Keys(Makefile.config for 2D Clustering) | ||
+ | $Config := ("CLUSTER":$CLUSTER) | | ||
+ | ("PADWIDTH":$PADWIDTH) | | ||
+ | ("PADHEIGHT":$PADHEIGHT) | | ||
+ | ("SHRINK":$SHRINK) | | ||
+ | ("PADMODE":$PADMODE) | | ||
+ | ("LowPassMode":$LowPassMode) | | ||
+ | ("LowPassResolution":$LowPassResolution) | | ||
+ | ("ClusterCorrelationMode":$ClusterCorrelationMode) | | ||
+ | ("ClusterRotationRangeMin":$ClusterRotationRangeMin) | | ||
+ | ("ClusterRotationRangeMax":$ClusterRotationRangeMax) | | ||
+ | ("ClusterRotationRangePartitionNumber":$ClusterRotationRangePartitionNumber) | | ||
+ | ("ClusterRotationIterationNumber":$ClusterRotationIterationNumber) | | ||
+ | ("ClusterRotationCorrelationMode":$ClusterRotationCorrelationMode) | | ||
+ | ("ClusterMode":$ClusterMode) | | ||
+ | ("TreeRootPositionX":$TreeRootPositionX) | | ||
+ | ("TreeRootPositionY":$TreeRootPositionY) | | ||
+ | ("TreeScaleX":$TreeScaleX) | | ||
+ | ("TreeScaleY":$TreeScaleY) | | ||
+ | ("TreeOffset":$TreeOffset) | ||
+ | </pre> | ||
+ | パラメータを設定しています。$TreeOffset := "1e1"のようにeを使用するような数値はPIONEの記法にはありませんので、" "で囲んで一旦文字列として設定しておきます。また、Makefile.configに書き出す処理を簡潔にするために設定したパラメータをキー付きシーケンスとして$Configに登録しています。新たにパラメータを追加したときは$Configにも追加するようにすれば後述で作成されるMakefile.configにも追加されるようになります。Configに値を直接登録することもできますが、実行ユーザが[[pione-client#オプション --params]]を利用してパラメータ設定できるようにparamで定義してから登録しています。<br> | ||
+ | <br> | ||
+ | |||
+ | では、Makefile.config作成用のルールを作成して、Mainに追加します。<br> | ||
+ | <pre> | ||
+ | Rule SetParam | ||
+ | output 'Makefile.config' | ||
+ | Action | ||
+ | key=({$Config.keys}) | ||
+ | value=({$Config.values}) | ||
+ | for (( i=0; i<{$Config.length}; i++ )) | ||
+ | do | ||
+ | echo "${key[${i}]}=${value[${i}]}" >> {$O[1]} | ||
+ | done | ||
+ | End | ||
+ | </pre> | ||
+ | それぞれのパラメータをキー付きシーケンスとしてConfigに登録しているので、[[PIONEの式#キー付きシーケンス]]のメソッドkeysとvaluesを利用してパラメータ名と値を得ることができますので、Makefile.configに書き込むことができます。<br> | ||
+ | <br> | ||
+ | |||
+ | SetParamで作成したMakefile.config | ||
+ | <pre> | ||
+ | CLUSTER=all | ||
+ | PADWIDTH=32 | ||
+ | PADHEIGHT=32 | ||
+ | SHRINK=1 | ||
+ | PADMODE=13 | ||
+ | LowPassMode=4 | ||
+ | LowPassResolution=0.1 | ||
+ | ClusterCorrelationMode=19 | ||
+ | ClusterRotationRangeMin=0 | ||
+ | ClusterRotationRangeMax=360 | ||
+ | ClusterRotationRangePartitionNumber=72 | ||
+ | ClusterRotationIterationNumber=2 | ||
+ | ClusterRotationCorrelationMode=0 | ||
+ | ClusterMode=2 | ||
+ | TreeRootPositionX=0 | ||
+ | TreeRootPositionY=400 | ||
+ | TreeScaleX=10 | ||
+ | TreeScaleY=100 | ||
+ | TreeOffset=1e1 | ||
+ | </pre> | ||
+ | 元のMakefile.configと同様のファイルを作成することができました。<br> | ||
+ | <br> | ||
+ | |||
+ | ==== 分類の点数付け ==== | ||
+ | [[単粒子解析#クラスター解析]]で行った分類されたグループと元のモデルとの一致数を算出する処理を追加してみましょう。<br> | ||
+ | <pre> | ||
+ | # For GroupingPoint | ||
+ | param $filename := "121p-shift2" | ||
+ | param $ROT2 := (0.upto(3)) * 45 | ||
+ | |||
+ | Rule GroupingPoint | ||
+ | input '*.lst'.all | ||
+ | input '*.tree'.all | ||
+ | output '{$CLUSTER}.groupinfo' | ||
+ | Action | ||
+ | array_rot2=( {$ROT2} ) | ||
+ | |||
+ | total=0 | ||
+ | element_total=0 | ||
+ | for num in {$I[1][1]} | ||
+ | do | ||
+ | element_num=$(wc -l ${num}.lst | awk '{print $1}') | ||
+ | element_total=$(( ${element_total} + ${element_num} )) | ||
+ | data=$(head -1 ${num}.tree) | ||
+ | root_i=$(echo ${data} | awk '{print $1}') | ||
+ | root_name=$(basename $(awk -v i=${root_i} '$2==i {print $1}' ${num}.lst) ".pad") | ||
+ | |||
+ | max=0 | ||
+ | max_i=0 | ||
+ | i=0 | ||
+ | include_max_rot2=${array_rot2[0]} | ||
+ | for rot2 in ${array_rot2[@]} | ||
+ | do | ||
+ | include_num=$(grep "{$filename}-0-${rot2}-" ${num}.lst | wc -l | awk '{print $1}') | ||
+ | if [ ${include_num} -gt ${max} ] ; then | ||
+ | max=${include_num} | ||
+ | include_max_rot2=${rot2} | ||
+ | max_i=${i} | ||
+ | fi | ||
+ | i=$(( ${i} + 1 )) | ||
+ | done | ||
+ | |||
+ | echo "group${root_i}: match: ${max} owner: ${include_max_rot2}" >> {$O[1]} | ||
+ | total=$(( ${total} + ${max} )) | ||
+ | |||
+ | unset array_rot2[${max_i}] | ||
+ | array_rot2=("${array_rot2[@]}") | ||
+ | done | ||
+ | echo " total: ${total}" >> {$O[1]} | ||
+ | echo "${total} ${element_total}" | awk '{printf("parcentage: %f\n", $1 / $2)}' >> {$O[1]} | ||
+ | End | ||
+ | </pre> | ||
+ | 今回、入力する使用ファイルはモデル有りで作成したサンプル画像のみを対象としています。(実際の[[電子顕微鏡]][[画像]]での分類の評価はもっと複雑で、手法としては[[クラスター解析]]や[[単粒子解析#クラスター解析]]で議論する内容)ファイル名がAAAA-0-Y-Z-S.roiのような画像(同ディレクトリMain_ROI.pioneで作成される)でYの回転の違いのみでグループ分けされることを前提として、モデルとの一致数を.groupファイルに出力します。上記のパラメータでは各グループをY回転0°, 45°, 90°, 135°の内、最も一致するグループに当てはめて、その一致数と割合を下記のように出力します。<br> | ||
+ | <br> | ||
+ | |||
+ | <pre> | ||
+ | group000022: match: 9 owner: 45 | ||
+ | group000013: match: 8 owner: 135 | ||
+ | group000028: match: 0 owner: 0 | ||
+ | group000001: match: 8 owner: 90 | ||
+ | total: 25 | ||
+ | parcentage: 0.625000 | ||
+ | </pre> | ||
+ | group0000NNのNNはグループ分けされたルートのNo.、owenerは最も一致した角度、matchは一致数をそれぞれ表します。またtotalは一致数の合計、percentageは割合です。<br> | ||
+ | <br> | ||
+ | |||
+ | 今回はYの回転のみで分類を評価していますが、パラメータやfor文、ログの記述部分などを追加することで、Xの回転も組み合わせて分類することは可能です。また、パラメータROT2は(0.upto(3)) * 45のように等間隔で角度を与えていますが、|で繋いで直接記述することでMain_ROIr.pioneで作成した画像のように等間隔でないファイルについても同様に評価することができます。<br> | ||
+ | <br> | ||
+ | <pre> | ||
+ | param $ROT2 := (17.888783 | 114.250336 | 173.771591 | 273.340057) | ||
+ | </pre> | ||
<br> | <br> |
2016年4月20日 (水) 07:16時点における最新版
PIONEチュートリアル
ここでは、PIONEのチュートリアルを行います。始める前にPIONEのインストールをしておきましょう。
目次
- 1 基本
- 1.1 基本1(特定のファイルを出力する)
- 1.2 基本2(特定のファイルを入力し、出力する(更新判定))
- 1.3 基本3(複数ファイルの入力と複数ファイルの出力)
- 1.4 基本4(複数ファイルの入力と複数ファイルの出力の並列処理)
- 1.5 基本5(直列:フロールールの設定 )
- 1.6 基本6(パラメータの定義)
- 1.7 基本7(条件文)
- 1.8 基本8(インタラクティブ操作)
- 1.9 基本9(インタラクションAPIの使用)
- 1.10 基本10(ループ文)
- 1.11 基本11(ループ文2)
- 1.12 基本12(チケット)
- 1.13 基本13(orの使用例)
- 1.14 基本14(例外)
- 1.15 基本15(ループ文3)
- 1.16 基本16(ランダム)
- 2 応用
基本
基本1(特定のファイルを出力する)
まず、PIONEを動かすにはルール定義書をつくる必要があります。
どんな言語でも最初に作成するHelloプログラムを作ってみましょう。
次の内容のファイル'HelloWorld.pione'を作成してみましょう。
Rule Main output 'message.txt' Action echo "Hello PIONE world !" > message.txt End
このファイルはこちらからダウンロードできます。
この後、pione-clientを実行します。
$ pione-client HelloWorld.pione -b helloOutput
例えば、次のような出力が流れます。
==> &Anonymous:Root([],{}) --> Rule Application: &Anonymous:Root([],{}) --> Distribution: &Anonymous:Root([],{}) >>> &Anonymous:Main([],{}) ==> &Anonymous:Main([],{}) SH ------------------------------------------------------------ SH echo "Hello PIONE world !" > message.txt SH ------------------------------------------------------------ <== &Anonymous:Main([],{}) <-- Distribution: &Anonymous:Root([],{}) <-- Rule Application: &Anonymous:Root([],{}) <== &Anonymous:Root([],{})
その結果、helloOutputというディレクトリができます。その中に指定したファイルmessage.txtが出力されています。
$ cat helloOutput/output/message.txt
Hello PIONE world !
もう一度、実行すると今度は、実行の必要がないために次のようなものが出力されます。
==> &Anonymous:Root([],{}) --> Rule Application: &Anonymous:Root([],{}) <-- Rule Application: &Anonymous:Root([],{}) <== &Anonymous:Root([],{})
さっきと比べると、Main Ruleが動いていないことが分かります。
さて、改めて設定したファイルを眺めてみます。
まず、最初に呼び出されるルール(Main)が定義されています。
Rule Main
Mainは、C言語などと同様に特別な意味をもつルールです。
次に、出力ファイルが定義されています。
output 'message.txt'
ここに書かれたファイルが最終的に-bで指定されたディレクトリに出力として戻ってきます。
Action以降が実際に起動するプログラムになります。
Action echo "Hello PIONE world !" > message.txt
実際にはシェルスクリプトが動きますので、どんなものも実行が可能です。
#!/bin/csh
で始めれば、cshを使って記述することもできますし、スクリプト系の言語であれば自由に記述し、実行形式を実行出来ます。
最後に、RuleをEndで終了します。
End
これが、一番シンプルなルールの書き方です。入力ファイルがないので、出力ファイルがなければ作成し、あれば作成しないという動作をします。
必要以上の動作をしないところが通常のシェルスクリプトを実行する場合と異なる点です。
基本2(特定のファイルを入力し、出力する(更新判定))
次に、入力ファイルから出力ファイルを作成する場合の定義書について作成してみましょう。
#Multiplying.pione Rule Main input 'test.in' output 'test.out' Action awk '{ print $1*2 }' {$I[1]} > {$O[1]} End
このファイルはこちらからダウンロードできます。
今回は、test.inという入力ファイルの中にある行頭の数字を2倍して、test.outというファイルを作り出すルールです (awkの使い方については、別途勉強してみて下さい)。
入力ファイルの更新判定により、ルールを実行するかどうかが変わってきます。
まず、
$ mkdir MultiplyingInput $ pione-client Multiplying.pione -b MultiplyingOutput -i MultiplyingInput/
として、入力ファイルがあるディレクトリ(指定しなければ、現在のディレクトリ)を指定して実行してみます。
==> &Anonymous:Root([],{}) --> Rule Application: &Anonymous:Root([],{}) <-- Rule Application: &Anonymous:Root([],{}) <== &Anonymous:Root([],{})
入力ファイルがありませんので、何もする事がないとして終了してしまいます。先ほどとの違いに気がついたでしょうか。
次に、ファイルを作成して、実行してみます。
$ echo "3" > MultiplyingInput/test.in $ echo "5" >> MultiplyingInput/test.in $ cat MultiplyingInput/test.in 3 5 $ pione-client Multiplying.pione -b MultiplyingOutput -i MultiplyingInput/ ==> &Anonymous:Root([test.in],{}) --> Rule Application: &Anonymous:Root([test.in],{}) --> Distribution: &Anonymous:Root([test.in],{}) >>> &Anonymous:Main([test.in],{}) ==> &Anonymous:Main([test.in],{}) SH ------------------------------------------------------------ SH awk '{ print $1*2 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ <-- Distribution: &Anonymous:Root([test.in],{}) <-- Rule Application: &Anonymous:Root([test.in],{}) <== &Anonymous:Main([test.in],{}) <== &Anonymous:Root([test.in],{}) $ cat MultiplyingOutput/output/test.out 6 10
となり、確かに2倍の値のファイルができあがっていることが分かります。
もう一度、実行すると、
$ pione-client Multiplying.pione -b MultiplyingOutput -i MultiplyingInput/ ==> &Anonymous:Root([test.in],{}) --> Rule Application: &Anonymous:Root([test.in],{}) <-- Rule Application: &Anonymous:Root([test.in],{}) <== &Anonymous:Root([test.in],{})
となり、ここでも何も実行しません。
つまり、入力ファイルに比べて出力ファイルのほうが新しいので、更新判定の結果、実行しなくてよいと判断したことになります。賢いですね。
ここで、ファイルを更新してみましょう。3行目に7を付け加えます。
$ echo "7" >> MultiplyingInput/test.in
そして、実行してみましょう。
$ pione-client Multiplying.pione -b MultiplyingOutput -i MultiplyingInput/ ==> &Anonymous:Root([test.in],{}) --> Rule Application: &Anonymous:Root([test.in],{}) --> Distribution: &Anonymous:Root([test.in],{}) >>> &Anonymous:Main([test.in],{}) ==> &Anonymous:Main([test.in],{}) SH ------------------------------------------------------------ SH awk '{ print $1*2 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ <-- Distribution: &Anonymous:Root([test.in],{}) <== &Anonymous:Main([test.in],{}) <-- Rule Application: &Anonymous:Root([test.in],{}) <== &Anonymous:Root([test.in],{}) $ cat MultiplyingOutput/output/test.out 6 10 14
今度は実行されました。これが更新判定によりルールが実行されるかどうかが判定されているということです。
もう一度実行しても、今度は実行されません。
$ pione-client Multiplying.pione -b MultiplyingOutput -i MultiplyingInput/ ==> &Anonymous:Root([test.in],{}) --> Rule Application: &Anonymous:Root([test.in],{}) <-- Rule Application: &Anonymous:Root([test.in],{}) <== &Anonymous:Root([test.in],{})
さて、出力ファイルを削除してみるとどうなるでしょうか。
$ rm MultiplyingOutput/output/test.out
今度は、実行されますね。
では、内容は変えずに、touch コマンドを使って、入力ファイルの修正時刻を変えるとどうなるでしょうか。
$ touch MultiplyingInput/test.in
今回も、予想通り、動作しました。
もし、動作しないようだったら、バグです。すぐに、GITHUB/PIONEに報告しましょう。
この基本2が理解できれば、まず、PIONEの動きの基本が理解できたことになります。
ところで、Actionで定義した動作の中に、見慣れない記号( {$I[1]}, {$O[1]} )が現れています。
Action awk '{ print $1*2 }' {$I[1]} > {$O[1]} End
{$I[1]}は入力ファイル、{$O[1]}は出力ファイルを表しています。それぞれの[]の中の数字1は、input/outputで定義したそれぞれの1番目ということを意味しています。
次の基本3での複数ファイルの入出力では1以外が使われることになります。
まだ、何が便利か少し分かりづらいと思いますが、例えば、Multiplying2.pioneとして、次のように記述するとどのように動作するでしょうか。
Rule Main input 'test.in' output '{$I[1]}.out' Action awk '{ print $1*2 }' {$I[1]} > {$O[1]} End
少し考えてみて下さい。そして、動作させてみましょう。
$ pione-client Multiplying2.pione -b MultiplyingOutput -i MultiplyingInput/
MultiplyingOutput/outputのディレクトリにはどんなファイルができあがったでしょうか。
そうですね。test.in.outができあがっています。何故かを考えてみましょう。
基本3(複数ファイルの入力と複数ファイルの出力)
さて、更新判定が理解できたところで、複数ファイルの入出力について考えてみましょう。
test1.in, test2.inから、それぞれ2倍した数が格納された、test1.out, test2.outが出力できるPIONE定義書を作成してみましょう。
ここで、名前をMultiplyingFiles.pioneとして作成してみましょう。
問題1:上記のMultiplyingFiles.pioneを作成して実行してみて下さい。
解答例1:
Rule Main input 'test1.in' input 'test2.in' output 'test1.out' output 'test2.out' Action awk '{ print $1*2 }' {$I[1]} > {$O[1]} awk '{ print $1*2 }' {$I[2]} > {$O[2]} End
このファイルはこちらからダウンロードできます。
どうでしょうか。思ったように動きましたか。複数のファイルがあれば、複数書き並べればよいのです。
でもなんだか、同じ事をするのに、同じような行を何行も書くのは嫌だなと思いませんか。もし、2倍を5倍に変えたくなったら全ての行を書き換えなくてはなりません。
10行ぐらいならそれでもよいけれど、100個、1000個とファイルが増えてきたらどうでしょうか。だんだん嫌になってきませんか。
では、どうすれば良いでしょうか。例えば、シェルスクリプトが得意なひとは次の様なプログラムを設定することも出来ます。
解答例2:
Rule Main input 'test1.in' input 'test2.in' output 'test1.out' output 'test2.out' Action for i in `ls *.in`; do awk '{ print $1*2 }' $i > `basename $i .in`.out done End
これでも、ファイルが増えるたびにinput/outputを書き換える必要があります。
そこで、全てのファイルを入力し、全てのファイルを出力するように入出力を表現することも出来ます。
解答例3:
Rule Main input '*.in'.all output '*.out'.all Action for i in `ls *.in`; do awk '{ print $1*2 }' $i > `basename $i .in`.out done End
どうでしょうか。うまく動作したでしょうか。ここで、
input '*.in'.all output '*.out'.all
の部分は、*は任意の文字列を表現します。つまり、*.inは、ファイルの最後が.inで終了しているファイル全て、*.outは、ファイルの最後が.outで終了しているファイル全てを表します。
.allがついていると、全てのファイルを取り扱うことを意味しています。
.allが付いていないときには、.eachがついていることがdefaultとして決まっています。一つひとつのファイルを別々に実行するという意味になります。
これでずいぶんと記述しやすくなりました。
でも、これだと、*.inのどれかのファイルが更新されていると全て変更になってしまいます。試してみて下さい。なんだか無駄ですね。
また、同時にできるはずのことをfor文を使って順次実行しているので、時間も無駄ですね。このくらいのタスクであれば、たいして時間がかかるわけではないのですが、もっと時間のかかるタスクだったらどうでしょうか。
必要なファイルの更新だけを、できれば複数のホストや一台でもマルチコアCPUを使って、同時に動かすともっと早く終了できるようになるはずです。
さて、それではどのようにすればよいのでしょうか。それが次の基本4になります。だんだんPIONEらしくなってきます。
基本4(複数ファイルの入力と複数ファイルの出力の並列処理)
次は、さっきと違ってallが付いていません。なにが起きるかを考えてみましょう。
Rule Main input '*.in' output '{$I[1][1]}.out' Action awk '{ print $1*2 }' {$I[1]} > {$O[1]} End
このファイルはこちらからダウンロードできます。
ここで、{$I[1][1]}.outは1番目のinputで拡張子を除いたファイル名を指します。(参照: PIONE定義書#入力定義)
下は実行した例です。
$ pione-client MultiplyingFilesEach.pione -b MultiplyingFilesEachOutput -i MultiplyingFilesInput/ -t 4 ==> &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) --> Rule Application: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) --> Distribution: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) >>> &Anonymous:Main([test1.in],{}) >>> &Anonymous:Main([test2.in],{}) >>> &Anonymous:Main([test3.in],{}) >>> &Anonymous:Main([test4.in],{}) >>> &Anonymous:Main([test5.in],{}) >>> &Anonymous:Main([test6.in],{}) >>> &Anonymous:Main([test7.in],{}) ==> &Anonymous:Main([test1.in],{}) ==> &Anonymous:Main([test2.in],{}) ==> &Anonymous:Main([test3.in],{}) ==> &Anonymous:Main([test4.in],{}) SH ------------------------------------------------------------ SH awk '{ print $1*2 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ SH ------------------------------------------------------------ SH awk '{ print $1*2 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ SH ------------------------------------------------------------ SH awk '{ print $1*2 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ SH ------------------------------------------------------------ SH awk '{ print $1*2 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ <== &Anonymous:Main([test2.in],{}) <== &Anonymous:Main([test1.in],{}) <== &Anonymous:Main([test3.in],{}) <== &Anonymous:Main([test4.in],{}) ==> &Anonymous:Main([test5.in],{}) ==> &Anonymous:Main([test6.in],{}) ==> &Anonymous:Main([test7.in],{}) SH ------------------------------------------------------------ SH awk '{ print $1*2 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ SH ------------------------------------------------------------ SH awk '{ print $1*2 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ SH ------------------------------------------------------------ SH awk '{ print $1*2 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ <== &Anonymous:Main([test6.in],{}) <== &Anonymous:Main([test5.in],{}) <== &Anonymous:Main([test7.in],{}) <-- Distribution: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) <-- Rule Application: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) <== &Anonymous:Root([test1.in,test2.in,test3.in,...],{})
4つのタスクが並列に動いている様子が分かるでしょうか。
確かに4つずつのタスク( ==> &Anonymous:Main([testN.in],{}))が並列に動作しているようです。
もう一度実行してみると下のようになります。
$ pione-client MultiplyingFilesEach.pione -b MultiplyingFilesEachOutput -i MultiplyingFilesInput/ -t 4 ==> &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) --> Rule Application: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) <-- Rule Application: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) <== &Anonymous:Root([test1.in,test2.in,test3.in,...],{})
今度は実行されません。そこで、ひとつファイルを更新してみます。
$ touch MultiplyingFilesInput/test3.in $ pione-client MultiplyingFilesEach.pione -b MultiplyingFilesEachOutput -i MultiplyingFilesInput/ -t 4 ==> &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) --> Rule Application: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) --> Distribution: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) >>> &Anonymous:Main([test3.in],{}) ==> &Anonymous:Main([test3.in],{}) SH ------------------------------------------------------------ SH awk '{ print $1*2 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ <== &Anonymous:Main([test3.in],{}) <-- Distribution: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) <-- Rule Application: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) <== &Anonymous:Root([test1.in,test2.in,test3.in,...],{})
となり、test3.inだけが更新されているのが分かります。これがeachとしての振る舞いです。
基本4の補足(eachでの速度検証)
eachを使った並列化処理による計算速度向上の一例を示しています。
基本5(直列:フロールールの設定 )
さて、もう少し複雑なルールについて設定してみましょう。
まず、ふたつのルールを組み合わせて、並列計算することを考えてみます。
Rule Main input '*.in' output '*.out' Flow rule First rule Second End Rule First input '*.in' output '{$I[1][1]}.route' Action awk '{ print $1*2 }' {$I[1]} > {$O[1]} End Rule Second input '*.route' output '{$I[1][1]}.out' Action awk '{ print $1+1 }' {$I[1]} > {$O[1]} End
このファイルはこちらからダウンロードできます。
実行結果を以下に示します。最初に5つのタスク(First)が並列で動いているのがわかります。
その後、終了した順に、次のルールが動いています。
$ pione-client Serial2.pione -i SerialInput/ -b Serial2Output ==> &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) --> Rule Application: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) --> Distribution: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) >>> &Anonymous:Main([test1.in],{}) >>> &Anonymous:Main([test2.in],{}) >>> &Anonymous:Main([test3.in],{}) >>> &Anonymous:Main([test4.in],{}) >>> &Anonymous:Main([test5.in],{}) ==> &Anonymous:Main([test1.in],{}) --> Rule Application: &Anonymous:Main([test1.in],{}) --> Distribution: &Anonymous:Main([test1.in],{}) >>> &Anonymous:First([test1.in],{}) ==> &Anonymous:Main([test2.in],{}) --> Rule Application: &Anonymous:Main([test2.in],{}) --> Distribution: &Anonymous:Main([test2.in],{}) >>> &Anonymous:First([test2.in],{}) ==> &Anonymous:Main([test3.in],{}) --> Rule Application: &Anonymous:Main([test3.in],{}) --> Distribution: &Anonymous:Main([test3.in],{}) >>> &Anonymous:First([test3.in],{}) ==> &Anonymous:Main([test4.in],{}) --> Rule Application: &Anonymous:Main([test4.in],{}) --> Distribution: &Anonymous:Main([test4.in],{}) >>> &Anonymous:First([test4.in],{}) ==> &Anonymous:Main([test5.in],{}) --> Rule Application: &Anonymous:Main([test5.in],{}) --> Distribution: &Anonymous:Main([test5.in],{}) >>> &Anonymous:First([test5.in],{}) ==> &Anonymous:First([test1.in],{}) SH ------------------------------------------------------------ SH awk '{ print $1*2 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ <== &Anonymous:First([test1.in],{}) <-- Distribution: &Anonymous:Main([test1.in],{}) --> Distribution: &Anonymous:Main([test1.in],{}) >>> &Anonymous:Second([test1.route],{}) ==> &Anonymous:First([test2.in],{}) SH ------------------------------------------------------------ SH awk '{ print $1*2 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ <== &Anonymous:First([test2.in],{}) <-- Distribution: &Anonymous:Main([test2.in],{}) --> Distribution: &Anonymous:Main([test2.in],{}) >>> &Anonymous:Second([test2.route],{}) ==> &Anonymous:First([test3.in],{}) SH ------------------------------------------------------------ SH awk '{ print $1*2 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ <== &Anonymous:First([test3.in],{}) <-- Distribution: &Anonymous:Main([test3.in],{}) --> Distribution: &Anonymous:Main([test3.in],{}) >>> &Anonymous:Second([test3.route],{}) ==> &Anonymous:First([test4.in],{}) SH ------------------------------------------------------------ SH awk '{ print $1*2 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ <== &Anonymous:First([test4.in],{}) <-- Distribution: &Anonymous:Main([test4.in],{}) --> Distribution: &Anonymous:Main([test4.in],{}) >>> &Anonymous:Second([test4.route],{}) ==> &Anonymous:First([test5.in],{}) SH ------------------------------------------------------------ SH awk '{ print $1*2 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ <== &Anonymous:First([test5.in],{}) <-- Distribution: &Anonymous:Main([test5.in],{}) --> Distribution: &Anonymous:Main([test5.in],{}) >>> &Anonymous:Second([test5.route],{}) ==> &Anonymous:Second([test1.route],{}) SH ------------------------------------------------------------ SH awk '{ print $1+1 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ <== &Anonymous:Second([test1.route],{}) <-- Distribution: &Anonymous:Main([test1.in],{}) <-- Rule Application: &Anonymous:Main([test1.in],{}) <== &Anonymous:Main([test1.in],{}) ==> &Anonymous:Second([test2.route],{}) SH ------------------------------------------------------------ SH awk '{ print $1+1 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ <== &Anonymous:Second([test2.route],{}) <-- Distribution: &Anonymous:Main([test2.in],{}) <-- Rule Application: &Anonymous:Main([test2.in],{}) <== &Anonymous:Main([test2.in],{}) ==> &Anonymous:Second([test3.route],{}) SH ------------------------------------------------------------ SH awk '{ print $1+1 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ <== &Anonymous:Second([test3.route],{}) <-- Distribution: &Anonymous:Main([test3.in],{}) <-- Rule Application: &Anonymous:Main([test3.in],{}) <== &Anonymous:Main([test3.in],{}) ==> &Anonymous:Second([test4.route],{}) SH ------------------------------------------------------------ SH awk '{ print $1+1 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ <== &Anonymous:Second([test4.route],{}) <-- Distribution: &Anonymous:Main([test4.in],{}) <-- Rule Application: &Anonymous:Main([test4.in],{}) <== &Anonymous:Main([test4.in],{}) ==> &Anonymous:Second([test5.route],{}) SH ------------------------------------------------------------ SH awk '{ print $1+1 }' {$I[1]} > {$O[1]} SH ------------------------------------------------------------ <== &Anonymous:Second([test5.route],{}) <-- Distribution: &Anonymous:Main([test5.in],{}) <-- Rule Application: &Anonymous:Main([test5.in],{}) <-- Distribution: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) <== &Anonymous:Main([test5.in],{}) <-- Rule Application: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) <== &Anonymous:Root([test1.in,test2.in,test3.in,...],{})
一度、終了しましたので、次は実行されません。
$ pione-client Serial2.pione -i SerialInput/ -b Serial2Output ==> &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) --> Rule Application: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) <-- Rule Application: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) <== &Anonymous:Root([test1.in,test2.in,test3.in,...],{})
さて、ここで、ファイルをひとつ(test6.in)増やしてみましょう。
$ pione-client Serial2.pione -i SerialInput/ -b Serial2Output ==> &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) --> Rule Application: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) --> Distribution: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) >>> &Anonymous:Main([test6.in],{}) ==> &Anonymous:Main([test6.in],{}) --> Rule Application: &Anonymous:Main([test6.in],{}) --> Distribution: &Anonymous:Main([test6.in],{}) >>> &Anonymous:First([test6.in],{}) ==> &Anonymous:First([test6.in],{}) SH ------------------------------------------------------------ SH awk '{ print $1*2 }' test6.in > test6.route SH ------------------------------------------------------------ <== &Anonymous:First([test6.in],{}) <-- Distribution: &Anonymous:Main([test6.in],{}) --> Distribution: &Anonymous:Main([test6.in],{}) >>> &Anonymous:Second([test6.route],{}) ==> &Anonymous:Second([test6.route],{}) SH ------------------------------------------------------------ SH awk '{ print $1+1 }' test6.route > test6.out SH ------------------------------------------------------------ <== &Anonymous:Second([test6.route],{}) <-- Distribution: &Anonymous:Main([test6.in],{}) <-- Rule Application: &Anonymous:Main([test6.in],{}) <-- Distribution: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) <== &Anonymous:Main([test6.in],{}) --> Distribution: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) <-- Distribution: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) <-- Rule Application: &Anonymous:Root([test1.in,test2.in,test3.in,...],{}) <== &Anonymous:Root([test1.in,test2.in,test3.in,...],{})
新しくできたファイルだけが作られていることがわかります。
それでは、ファイルの更新がかかったときはどうなるでしょうか。test2.inをtouchコマンドで更新してみました。
$ touch SerialInput/test2.in $ pione-client Serial2.pione -i SerialInput/ -b Serial2Output2 ==> &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) --> Rule Application: &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) --> Distribution: &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) >>> &Anonymous:Main([test2.in],{}) >>> &Anonymous:Main([test6.in],{}) ==> &Anonymous:Main([test2.in],{}) --> Rule Application: &Anonymous:Main([test2.in],{}) --> Distribution: &Anonymous:Main([test2.in],{}) >>> &Anonymous:First([test2.in],{}) ==> &Anonymous:Main([test6.in],{}) --> Rule Application: &Anonymous:Main([test6.in],{}) <-- Rule Application: &Anonymous:Main([test6.in],{}) <== &Anonymous:Main([test6.in],{}) ==> &Anonymous:First([test2.in],{}) SH ------------------------------------------------------------ SH awk '{ print $1*2 }' test2.in > test2.route SH ------------------------------------------------------------ <== &Anonymous:First([test2.in],{}) <-- Distribution: &Anonymous:Main([test2.in],{}) --> Distribution: &Anonymous:Main([test2.in],{}) >>> &Anonymous:Second([test2.route],{}) ==> &Anonymous:Second([test2.route],{}) SH ------------------------------------------------------------ SH awk '{ print $1+1 }' test2.route > test2.out SH ------------------------------------------------------------ <== &Anonymous:Second([test2.route],{}) <-- Distribution: &Anonymous:Main([test2.in],{}) <-- Rule Application: &Anonymous:Main([test2.in],{}) <-- Distribution: &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) <== &Anonymous:Main([test2.in],{}) --> Distribution: &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) >>> &Anonymous:Main([test6.in],{}) ==> &Anonymous:Main([test6.in],{}) --> Rule Application: &Anonymous:Main([test6.in],{}) <-- Rule Application: &Anonymous:Main([test6.in],{}) <== &Anonymous:Main([test6.in],{}) <-- Distribution: &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) --> Distribution: &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) <-- Distribution: &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) <-- Rule Application: &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{}) <== &Anonymous:Root([.DS_Store,test1.in,test2.in,...],{})
となり、新しいファイルであるtest2.in, test6.inに関しては、変更の可能性があるので起動し始めます。
しかし、実際にinputファイルが更新されたtest2.inのみ処理が行われ、test2.outのみ作成されます。
基本5の補足(並列処理での速度向上例)
基本5のプログラムに関して速度向上の例を示しています。
基本6(パラメータの定義)
入力データは、ファイルから読み取る以外にパラメータとして使用する方法があります。
パラメータにはbasicとadvancedの2つがあります。処理上の違いは特にありませんが、前者を基本パラメータ、後者を上級者向けパラメータとして定義することでユーザのレベルに応じて変更可能なパラメータを区別することができます。
下記のように定義します。(参照:PIONE定義書#パラメータ定義)
param $val := 123 basic param $b_val := 456 advanced param $a_val := 987 Basic Param $b_val1 := 135 $b_val2 := 246 End Advanced Param $a_val1 := 975 $a_val2 := 864 End Rule Main output 'message.txt' param $main_val := $val * 10 Flow rule Sub {sub_val1: $main_val} End Rule Sub output 'message.txt' param $sub_val1 param $sub_val2 := $val * 100 Action echo "Basic Parameters:" > {$O[1]}; echo " val: {$val}" >> {$O[1]}; echo " b_val: {$b_val}" >> {$O[1]}; echo " b_val1: {$b_val1}" >> {$O[1]}; echo " b_val2: {$b_val2}" >> {$O[1]}; echo "Advanced Parameters:" >> {$O[1]}; echo " a_val: {$a_val}" >> {$O[1]}; echo " a_val1: {$a_val1}" >> {$O[1]}; echo " a_val2: {$a_val2}" >> {$O[1]}; echo "Parameters in Sub:" >> {$O[1]}; echo " sub_val1: {$sub_val1}" >> {$O[1]}; echo " sub_val2: {$sub_val2}" >> {$O[1]}; End
1つずつ定義するときはそれぞれbasic param XXX、advanced param YYYのように記述します。(単にparam ZZZとしたときはbasic扱いとなります。)
まとめて定義したいときはBasic Param ~ End, Advanced Param ~ Endで囲みます。
定義する変数名の先頭には$を付けて記述し、:=にてデフォルト値を定義することができます。
またAction内で使用するときは入力ファイルや出力ファイルと同様に{ }で括ります。
設定したパラメータはオプション--params="{XXX:N}"で値を設定して実行します。
また、定義書内でデフォルト値を記述しておけば、--params内で設定しなかったパラメータもデフォルト値で処理されます。
今回はbasicのみに値を設定して実行してみます。
/Basic6$ pione-client ParamEcho.pione -b ParamEcho --params="{val:1,b_val:3,b_val1:5,b_val2:7}" "{val:1,b_val:3,b_val1:5,b_val2:7}" ==> &Anonymous:Root([],{}) --> Rule Application: &Anonymous:Root([],{}) --> Distribution: &Anonymous:Root([],{}) >>> &Anonymous:Main([],{main_val:(<i>10)}) ==> &Anonymous:Main([],{main_val:(<i>10)}) --> Rule Application: &Anonymous:Main([],{main_val:(<i>10)}) --> Distribution: &Anonymous:Main([],{main_val:(<i>10)}) >>> &Anonymous:Sub([],{sub_val1:(<i>10),sub_val2:(<i>100)}) ==> &Anonymous:Sub([],{sub_val1:(<i>10),sub_val2:(<i>100)}) SH ------------------------------------------------------------ SH echo "Basic Parameters:" > message.txt; SH echo " val: 1" >> message.txt; SH echo " b_val: 3" >> message.txt; SH echo " b_val1: 5" >> message.txt; SH echo " b_val2: 7" >> message.txt; SH echo "Advanced Parameters:" >> message.txt; SH echo " a_val: 987" >> message.txt; SH echo " a_val1: 975" >> message.txt; SH echo " a_val2: 864" >> message.txt; SH echo "Parameters in Sub:" >> message.txt; SH echo " sub_val1: 10" >> message.txt; SH echo " sub_val2: 100" >> message.txt; SH ------------------------------------------------------------ <== &Anonymous:Sub([],{sub_val1:(<i>10),sub_val2:(<i>100)}) <-- Distribution: &Anonymous:Main([],{main_val:(<i>10)}) <-- Rule Application: &Anonymous:Main([],{main_val:(<i>10)}) <-- Distribution: &Anonymous:Root([],{}) <== &Anonymous:Main([],{main_val:(<i>10)}) <-- Rule Application: &Anonymous:Root([],{}) <== &Anonymous:Root([],{}) /Basic6$ cat ParamEcho/output/message.txt Basic Parameters: val: 1 b_val: 3 b_val1: 5 b_val2: 7 Advanced Parameters: a_val: 987 a_val1: 975 a_val2: 864 Parameters in Sub: sub_val1: 10 sub_val2: 100 /Basic6$
設定を反映した処理が実行できました。
パラメータのシーケンス
ではパラメータの値を複数(シーケンス)使用した場合はどうなるのでしょうか。
valに複数の値(1|2)を入れてみます。(参照:PIONEの式#シーケンス)
/Basic6$ pione-client ParamEcho.pione -b ParamEcho --params="{val:(1|2)}" "{val:(1|2)}" ==> &Anonymous:Root([],{}) --> Rule Application: &Anonymous:Root([],{}) --> Distribution: &Anonymous:Root([],{}) >>> &Anonymous:Main([],{main_val:(<i>10)}) >>> &Anonymous:Main([],{main_val:(<i>20)}) ==> &Anonymous:Main([],{main_val:(<i>10)}) --> Rule Application: &Anonymous:Main([],{main_val:(<i>10)}) --> Distribution: &Anonymous:Main([],{main_val:(<i>10)}) >>> &Anonymous:Sub([],{sub_val1:(<i>10),sub_val2:(<i>100)}) >>> &Anonymous:Sub([],{sub_val1:(<i>10),sub_val2:(<i>200)}) ==> &Anonymous:Main([],{main_val:(<i>20)}) --> Rule Application: &Anonymous:Main([],{main_val:(<i>20)}) --> Distribution: &Anonymous:Main([],{main_val:(<i>20)}) >>> &Anonymous:Sub([],{sub_val1:(<i>20),sub_val2:(<i>100)}) >>> &Anonymous:Sub([],{sub_val1:(<i>20),sub_val2:(<i>200)}) ==> &Anonymous:Sub([],{sub_val1:(<i>10),sub_val2:(<i>100)}) SH ------------------------------------------------------------ SH echo "Basic Parameters:" > message.txt; SH echo " val: 1 2" >> message.txt; SH echo " b_val: 456" >> message.txt; SH echo " b_val1: 135" >> message.txt; SH echo " b_val2: 246" >> message.txt; SH echo "Advanced Parameters:" >> message.txt; SH echo " a_val: 987" >> message.txt; SH echo " a_val1: 975" >> message.txt; SH echo " a_val2: 864" >> message.txt; SH echo "Parameters in Sub:" >> message.txt; SH echo " sub_val1: 10" >> message.txt; SH echo " sub_val2: 100" >> message.txt; SH ------------------------------------------------------------ <== &Anonymous:Sub([],{sub_val1:(<i>10),sub_val2:(<i>100)}) ==> &Anonymous:Sub([],{sub_val1:(<i>10),sub_val2:(<i>200)}) SH ------------------------------------------------------------ SH echo "Basic Parameters:" > message.txt; SH echo " val: 1 2" >> message.txt; SH echo " b_val: 456" >> message.txt; SH echo " b_val1: 135" >> message.txt; SH echo " b_val2: 246" >> message.txt; SH echo "Advanced Parameters:" >> message.txt; SH echo " a_val: 987" >> message.txt; SH echo " a_val1: 975" >> message.txt; SH echo " a_val2: 864" >> message.txt; SH echo "Parameters in Sub:" >> message.txt; SH echo " sub_val1: 10" >> message.txt; SH echo " sub_val2: 200" >> message.txt; SH ------------------------------------------------------------ <== &Anonymous:Sub([],{sub_val1:(<i>10),sub_val2:(<i>200)}) <-- Distribution: &Anonymous:Main([],{main_val:(<i>10)}) <-- Rule Application: &Anonymous:Main([],{main_val:(<i>10)}) <== &Anonymous:Main([],{main_val:(<i>10)}) ==> &Anonymous:Sub([],{sub_val1:(<i>20),sub_val2:(<i>100)}) SH ------------------------------------------------------------ SH echo "Basic Parameters:" > message.txt; SH echo " val: 1 2" >> message.txt; SH echo " b_val: 456" >> message.txt; SH echo " b_val1: 135" >> message.txt; SH echo " b_val2: 246" >> message.txt; SH echo "Advanced Parameters:" >> message.txt; SH echo " a_val: 987" >> message.txt; SH echo " a_val1: 975" >> message.txt; SH echo " a_val2: 864" >> message.txt; SH echo "Parameters in Sub:" >> message.txt; SH echo " sub_val1: 20" >> message.txt; SH echo " sub_val2: 100" >> message.txt; SH ------------------------------------------------------------ <== &Anonymous:Sub([],{sub_val1:(<i>20),sub_val2:(<i>100)}) ==> &Anonymous:Sub([],{sub_val1:(<i>20),sub_val2:(<i>200)}) SH ------------------------------------------------------------ SH echo "Basic Parameters:" > message.txt; SH echo " val: 1 2" >> message.txt; SH echo " b_val: 456" >> message.txt; SH echo " b_val1: 135" >> message.txt; SH echo " b_val2: 246" >> message.txt; SH echo "Advanced Parameters:" >> message.txt; SH echo " a_val: 987" >> message.txt; SH echo " a_val1: 975" >> message.txt; SH echo " a_val2: 864" >> message.txt; SH echo "Parameters in Sub:" >> message.txt; SH echo " sub_val1: 20" >> message.txt; SH echo " sub_val2: 200" >> message.txt; SH ------------------------------------------------------------ <== &Anonymous:Sub([],{sub_val1:(<i>20),sub_val2:(<i>200)}) <-- Distribution: &Anonymous:Main([],{main_val:(<i>20)}) <-- Rule Application: &Anonymous:Main([],{main_val:(<i>20)}) <-- Distribution: &Anonymous:Root([],{}) <== &Anonymous:Main([],{main_val:(<i>20)}) <-- Rule Application: &Anonymous:Root([],{}) <== &Anonymous:Root([],{}) /Basic6$
デフォルトではval=(1|2)がeachとして動作します。この場合はmain_val=(10|20)となって、それぞれsub_val1=(10|20), sub_val2=(100|200)となります。
このとき、sub_val1, sub_val2はともにeachですので、上記のように(10, 100), (10, 200), (20, 100), (20, 200)と2*2の組み合わせでルールが動きます。
では、val=(1|2).allではどうなるでしょうか。
/Basic6$ pione-client ParamEcho.pione -b ParamEcho --params="{val:(1|2).all}" "{val:(1|2).all}" ==> &Anonymous:Root([],{}) --> Rule Application: &Anonymous:Root([],{}) --> Distribution: &Anonymous:Root([],{}) >>> &Anonymous:Main([],{main_val:(<i>10|20)}) ==> &Anonymous:Main([],{main_val:(<i>10|20)}) --> Rule Application: &Anonymous:Main([],{main_val:(<i>10|20)}) --> Distribution: &Anonymous:Main([],{main_val:(<i>10|20)}) >>> &Anonymous:Sub([],{sub_val1:(<i>10|20),sub_val2:(<i>100|200)}) ==> &Anonymous:Sub([],{sub_val1:(<i>10|20),sub_val2:(<i>100|200)}) SH ------------------------------------------------------------ SH echo "Basic Parameters:" > message.txt; SH echo " val: 1 2" >> message.txt; SH echo " b_val: 456" >> message.txt; SH echo " b_val1: 135" >> message.txt; SH echo " b_val2: 246" >> message.txt; SH echo "Advanced Parameters:" >> message.txt; SH echo " a_val: 987" >> message.txt; SH echo " a_val1: 975" >> message.txt; SH echo " a_val2: 864" >> message.txt; SH echo "Parameters in Sub:" >> message.txt; SH echo " sub_val1: 10 20" >> message.txt; SH echo " sub_val2: 100 200" >> message.txt; SH ------------------------------------------------------------ <== &Anonymous:Sub([],{sub_val1:(<i>10|20),sub_val2:(<i>100|200)}) <-- Distribution: &Anonymous:Main([],{main_val:(<i>10|20)}) <-- Rule Application: &Anonymous:Main([],{main_val:(<i>10|20)}) <== &Anonymous:Main([],{main_val:(<i>10|20)}) <-- Distribution: &Anonymous:Root([],{}) <-- Rule Application: &Anonymous:Root([],{}) <== &Anonymous:Root([],{}) /Basic6$
allの場合はsub_val1=(10|20), sub_val2=(100|200)として組み合わせるので、上記のように1つのルールが動くだけです。
このようにパラメータについてもeach, allを使うことでルールの振り分けが可能です。
基本7(条件文)
if文
今回はif文などを用いて実行したいルールを制御してみましょう。
param $val := 123 Rule Main output 'message.txt' Flow if $val % 2 == 0 rule Even else rule Odd end End Rule Even output 'message.txt' Action echo "{$val} is even." > message.txt; End Rule Odd output 'message.txt' Action echo "{$val} is odd." > message.txt; End
このファイルはこちらからダウンロードできます。
最初のパラメータvalに対して、偶数の場合と奇数の場合でそれぞれ別のルールを実行するようにしています。
Rule Mainにif文がありますが、PIONEの記述ではif ~ endと記述します。
では、valをデフォルト値で実行してみましょう。
/Basic7$ pione-client EvenOdd.pione -b EvenOdd ==> &Anonymous:Root([],{}) --> Rule Application: &Anonymous:Root([],{}) --> Distribution: &Anonymous:Root([],{}) >>> &Anonymous:Main([],{}) ==> &Anonymous:Main([],{}) --> Rule Application: &Anonymous:Main([],{}) --> Distribution: &Anonymous:Main([],{}) >>> &Anonymous:Odd([],{}) ==> &Anonymous:Odd([],{}) SH ------------------------------------------------------------ SH echo "123 is odd." > message.txt; SH ------------------------------------------------------------ <== &Anonymous:Odd([],{}) <-- Distribution: &Anonymous:Main([],{}) <-- Rule Application: &Anonymous:Main([],{}) <-- Distribution: &Anonymous:Root([],{}) <== &Anonymous:Main([],{}) <-- Rule Application: &Anonymous:Root([],{}) <== &Anonymous:Root([],{}) /Basic7$ cat EvenOdd/output/message.txt 123 is odd. /Basic7$
val=123で定義されていますので、この場合は奇数のルールが動いているのが分かります。
次にvalを偶数にして実行してみます。
/Basic7$ pione-client EvenOdd.pione -b EvenOdd --params="{val:456}" "{val:456}" ==> &Anonymous:Root([],{}) --> Rule Application: &Anonymous:Root([],{}) --> Distribution: &Anonymous:Root([],{}) >>> &Anonymous:Main([],{}) ==> &Anonymous:Main([],{}) --> Rule Application: &Anonymous:Main([],{}) --> Distribution: &Anonymous:Main([],{}) >>> &Anonymous:Even([],{}) ==> &Anonymous:Even([],{}) SH ------------------------------------------------------------ SH echo "456 is even." > message.txt; SH ------------------------------------------------------------ <== &Anonymous:Even([],{}) <-- Distribution: &Anonymous:Main([],{}) <-- Rule Application: &Anonymous:Main([],{}) <-- Distribution: &Anonymous:Root([],{}) <== &Anonymous:Main([],{}) <-- Rule Application: &Anonymous:Root([],{}) <== &Anonymous:Root([],{}) /Basic7$ cat EvenOdd/output/message.txt 456 is even. /Basic7$
偶数のルールが動きます。
以上のようにして条件に合わせてルールを制御することができます。
case文
case文の場合は下記のように記述します。
case $val % 2 when 0 rule Even else rule Odd end
constraint
constraintを使用して下記のように記述する方法もあります。
param $val := 123 Rule Main output 'message.txt' Flow rule Even rule Odd End Rule Even output 'message.txt' constraint $val % 2 == 0 Action echo "{$val} is even." > {$O[1]}; End Rule Odd output 'message.txt' constraint $val % 2 == 1 Action echo "{$val} is odd." > {$O[1]}; End
デバッグのための条件文
条件文を使って、デバッグの切り替えの手間が少なくなる方法を考えてみましょう。
基本8(インタラクティブ操作)
ここまでは、コマンドを実行すると結果出力までの動作を全てプログラムに委ねる、一方通行の処理を記述してきました。
次は、処理の途中でユーザが操作できるようにし、それ以降の処理を制御(インタラクティブ操作)できるようにしてみましょう。
今回はウェブページから選択した入力ファイルに対して、設定した演算子(+, *)、数値で計算したファイルを出力する処理を作成します。
実行ファイル作成のためにパッケージを、実行のためにPIONE Webclientを利用します。
まず、インタラクティブ操作のための処理をPIONE定義書に記述します。
.@ PackageName :: "InteractiveCalc" Rule Main output '*.out'.all Flow rule Interaction End Rule Interaction output '*.out'.all Action # build public directory for pione-interactive mkdir public cp etc/* public cp bin/* public echo "Interruption!" > result.log.out # start interactive operation pione-interactive browser --public public cp public/* . echo "finish!" > result.log.out End
この処理では、.outファイルを出力ファイルとしてダウンロードできるようにしています。
pione-interactive browserコマンドにてInteractiveページを開けるようにします。
このとき、--publicオプションで、ページのために使用するhtmlファイルやcgiファイルなどがあるディレクトリを指定します。
そのために、事前にディレクトリ(public)を用意し、そのディレクトリの下に、etcディレクトリ内とbinディレクトリ内のすべてのファイルをそれぞれコピーしています。
また、Interactiveページを終了した後は、作成したファイルを処理できるようにpublicディレクトリ内のデータを全てワークスペース(.)へコピーしています。
次に、ウェブページにて最初に呼び出されるindex.htmlを作成します。htmlファイルはetcディレクトリ内に置きます。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>InteractiveCalc Index Page</title> </head> <body> <form action="FileCalc.cgi" method="post" enctype="multipart/form-data"> 入力ファイル名 <input type="file" name="inputfile"/> <br> 演算子 <select name="operator"> <option value="add">+</option> <option value="mul">*</option> </select> 数値 <input type="text" name="value"/> <br> 出力ファイル名 <input type="text" name="outputfile"/> <br> <button type="submit">計算開始</button> </form> <br> <br> <a href="?pione-action=finish">終了</a> </body> </html>
最後の方にある<a href="?pione-action=finish">終了</a>は、「終了」をクリックするとワーキンングディレクトリに終了通知を行い、インタラクティブ操作を終了させる処理です。詳しくはインタラクションAPIをご覧下さい。
そして、.cgiファイルを作成します。シェルスクリプトやEosのコマンドなどの特殊な処理はこのファイルに記述します。
cgiファイルはbinディレクトリ内に置きます。
#!/usr/bin/env ruby require 'cgi' cgi = CGI.new HTMLstr="" # Main Process ## Query to Parameters input_file=cgi.params['inputfile'][0] output_path=cgi['outputfile'] operator=cgi['operator'] value=cgi['value'].to_i ## Calc input to output output_file = open(output_path, "w") HTMLstr += "<table>" HTMLstr += "<tr>" HTMLstr += "<td>" + input_file.original_filename.to_s + "</td>" HTMLstr += "<td>" + output_path + "</td>" HTMLstr += "</tr>" input_file.each do |input_line| HTMLstr += "<tr>" HTMLstr += "<td>" + input_line + "</td>" work_value = input_line.to_i case operator when "add" work_value += value when "mul" work_value *= value end output_line = work_value.to_s output_file.write(output_line + "\n") HTMLstr += "<td>" + output_line + "</td>" HTMLstr += "</tr>" end HTMLstr += "</table>" output_file.close # Output as html cgi.out(type: "text/html") do HTMLstr end
今回はRubyを使って記述しています。# Output as html以下のコードでhtml形式に変換するようにしています。そのためにCGIクラスが必要なので、require 'cgi'とcgi = CGI.newで用意しています。このようにすると、# Main Process内のようにHTMLstrに記述する文字列を加えるだけで、自由な処理を行ってその結果をhtmlのページを表示することができます。また、## Query to ParametersにてPOST形式のクエリを変数に変換していますが、typeがfileの場合、ファイル本体はCGIクラスメンバーParamsから受け取ります。このとき受け取るデータはファイルポインタ配列ですので、取得したい要素No.を指定する必要があります。今回、入力ファイルは1つしか格納していないので、cgi.params['inputfile'][0]となります。
コードが完成したら、パッケージ作成を行います。(PIONEチュートリアル-packageを参照)
なお、これらのファイルはこちらからダウンロードできます。
/PIONE$ pione package build Basic8/ info: update the package info file: local:/Eos/tutorial/SampleCode/PIONE/Basic8/pione-package.json info: Package local:/Eos/tutorial/SampleCode/PIONE/InteractiveCalc.ppg has been built successfully.
作成したパッケージはPIONE Webclientにて動かすことができます。PIONE Webclientチュートリアル#インタラクティブ操作を含むコマンドを実行にて動作方法と実行結果を記載しています。
index.htmlとFileCalc.cgiで作成したページ
-> |
基本9(インタラクションAPIの使用)
インタラクションAPIを使用してワーキングディレクトリに通知を送ることで、ファイル操作や終了などを命令することができます。.htmlや.cgiに記述することにより、ユーザからの操作を受けてこれらの命令を実行することができます。
Annotation.pione
.@ PackageName :: "FileOperation" .@ Editor :: "Kinoshita" .@ Tag :: "v0.1.0"
Main.pione
Rule Main output '*.txt'.all Flow rule Interaction End
今回は.txtファイルのみを出力するようにしています。
Interaction.pione
Rule Interaction output '*.*'.all Action # build public directory for pione-interactive mkdir public cp etc/* public cp bin/* public # start interactive operation pione-interactive browser --public public cp public/* . End
etc/index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>FileOperation</title> </head> <body> <form action="./AAA.txt" method="post" enctype="multipart/form-data"> <input type="hidden" name="pione-action" value="create"/> <button type="submit">作成(ファイル)</button> <input type="file" name="pione-content" value=""/> </form> <form action="./AAA.txt" method="post" enctype="multipart/form-data"> <input type="hidden" name="pione-action" value="create"/> <button type="submit">作成(テキスト)</button> <input type="text" name="pione-content" value=""/> </form> <form action="./AAA.txt" method="post" enctype="multipart/form-data"> <input type="hidden" name="pione-action" value="delete"/> <button type="submit">削除</button> </form> <form action="./list.cgi" method="post" enctype="multipart/form-data"> <input type="hidden" name="pione-action" value="get"/> <button type="submit">ファイル一覧の取得</button> </form> <br> <br> <a href="?pione-action=finish">終了</a> </body> </html>
それぞれのボタンよりファイル操作を行います。
作成(ファイル): | 指定したファイルをサーバ上のファイル(AAA.txt)に書き込んでアップロードします。 |
作成(テキスト): | 指定したテキストボックスの内容でサーバ上のファイル(AAA.txt)に書き込みます。 |
削除: | サーバ上のファイル(AAA.txt)内容を削除します。 |
ファイル一覧の取得: | サーバ上のファイル一覧を表示します。(list.cgiが動作します) |
終了: | 操作を終了します。 |
etc/list.cgi
#!/usr/bin/env ruby require 'cgi' cgi = CGI.new strHTML = "" strHTML += <<'Block-HTML' <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>CGIページ</title> </head> <body> <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script> <div id="textDiv"></div> <script type="text/javascript"> var div = document.getElementById("textDiv"); div.textContent = ""; $.getJSON("./", {"pione-action": "list"}, function(data){ $.each(data, function(file) { div.textContent += this.name +"\n"; }); }); </script> <a href="Index.html">戻る</a> <br> <br> <a href="?pione-action=finish">終了</a> </body> </html> Block-HTML # Output as html cgi.out(type: "text/html") do strHTML end
"./"に対して"pione-action=list"を送信したときの戻り値をgetJSONにより取得して、データ毎の"name"を出力するようにしています。
これらのファイルはこちらからダウンロードできます。
では、実行させてみましょう。
まずは選択...ボタンからテキストファイルを選択し、作成(ファイル)ボタンでサーバ内にファイルを作成(アップロード)してみましょう。
これによりAAA.txtが同じ内容で作成(アップロード)されます。終了した後にダウンロードすると/output/AAA.txtの内容を確認できます。
また、ファイル一覧の取得ボタンよりサーバ上のファイル一覧を閲覧することができます。
AAA.txtが確認できます。
そして削除ボタンを押すと、AAA.txtを削除します。
AAA.txtがなくなっているのが確認できます。
次はテキストボックスに文字列を書き込んで、作成(テキスト)ボタンを押してみましょう。
これによりAAA.txtがテキストの内容で作成されます。
操作を終えるときには終了をクリックして下さい。終了通知が送られてインタラクティブ処理が完了します。
また、htmlなどを実装する上で終了時には必ず終了通知finishを送信するように注意しましょう。
基本10(ループ文)
PIONE定義書にてループ文を作成してみましょう。
Action内はシェルスクリプトですので、for文やwhile文を使用することで容易にループを実現できます。
しかし、Actionに記述した処理は一つのルールに書かれるため、一かたまりの処理として一つのマシンで実行されてしまいます。
それでは、処理を分割できるPIONEの長所を活かせていません。
ループ内の一つひとつの処理を分割し、複数のマシンで短時間に処理を行うことを考えましょう。
そこで、Flowでループ文に相当する内容を作成し、ループ内の個別の処理を各々のマシンに振り分ける工夫を施します。
今回は、#基本7(制御文)での偶数奇数判定の処理を、最小値から最大値まで加算値を加算しながら行っていくプログラムを作成します。
このプログラムはこちらからダウンロードできます。
最大値、最小値、加算値をパラメータで設定できるようにし、ルールLoopSystemにてループ文を実現しています。
param $maxval := 456 param $minval := 123 param $dval := 37 Rule Main output '*.txt' Flow rule LoopSystem {val: $minval} End Rule LoopSystem output '*.txt' param $val Flow if $val <= $maxval rule EvenOdd {val: $val} end if ($val + $dval) <= $maxval rule LoopSystem {val: $val + $dval} end End Rule EvenOdd output '*.txt' param $val Flow if $val % 2 == 0 rule Even {val: $val} else rule Odd {val: $val} end End Rule Even output '*.txt' param $val Action echo "{$val} is even." >> Even{$val}.txt; End Rule Odd output '*.txt' Action echo "{$val} is odd." >> Odd{$val}.txt; End
まず、MainルールからLoopSystemをval=最小値で呼び出し、その中でvalが最大値になるまでは加算値を加えながら再帰的にLoopSystemを呼び出しています。
出力ファイル名を偶数のときはEven{$val}.txt、奇数のときはOdd{$val}.txtとしています。
では、実行してみます。
/Basic10$ pione-client EvenOddLoop.pione ==> &Anonymous:Root([],{}) --> Rule Application: &Anonymous:Root([],{}) --> Distribution: &Anonymous:Root([],{}) >>> &Anonymous:Main([],{}) ==> &Anonymous:Main([],{}) --> Rule Application: &Anonymous:Main([],{}) --> Distribution: &Anonymous:Main([],{}) >>> &Anonymous:LoopSystem([],{val:(<i>123)}) ==> &Anonymous:LoopSystem([],{val:(<i>123)}) --> Rule Application: &Anonymous:LoopSystem([],{val:(<i>123)}) --> Distribution: &Anonymous:LoopSystem([],{val:(<i>123)}) >>> &Anonymous:EvenOdd([],{val:(<i>123)}) >>> &Anonymous:LoopSystem([],{val:(<i>160)}) ==> &Anonymous:EvenOdd([],{val:(<i>123)}) --> Rule Application: &Anonymous:EvenOdd([],{val:(<i>123)}) --> Distribution: &Anonymous:EvenOdd([],{val:(<i>123)}) >>> &Anonymous:Odd([],{val:(<i>123)}) ==> &Anonymous:LoopSystem([],{val:(<i>160)}) --> Rule Application: &Anonymous:LoopSystem([],{val:(<i>160)}) --> Distribution: &Anonymous:LoopSystem([],{val:(<i>160)}) >>> &Anonymous:EvenOdd([],{val:(<i>160)}) >>> &Anonymous:LoopSystem([],{val:(<i>197)}) ==> &Anonymous:Odd([],{val:(<i>123)}) SH ------------------------------------------------------------ SH echo "123 is odd." >> Odd123.txt; SH ------------------------------------------------------------ <== &Anonymous:Odd([],{val:(<i>123)}) -中略- ==> &Anonymous:Even([],{val:(<i>456)}) SH ------------------------------------------------------------ SH echo "456 is even." >> Even456.txt; SH ------------------------------------------------------------ <== &Anonymous:Even([],{val:(<i>456)}) <-- Distribution: &Anonymous:EvenOdd([],{val:(<i>456)}) <-- Rule Application: &Anonymous:EvenOdd([],{val:(<i>456)}) <== &Anonymous:EvenOdd([],{val:(<i>456)}) -中略- <-- Distribution: &Anonymous:LoopSystem([],{val:(<i>160)}) <-- Rule Application: &Anonymous:LoopSystem([],{val:(<i>160)}) <== &Anonymous:LoopSystem([],{val:(<i>160)}) <-- Distribution: &Anonymous:LoopSystem([],{val:(<i>123)}) <-- Rule Application: &Anonymous:LoopSystem([],{val:(<i>123)}) <== &Anonymous:LoopSystem([],{val:(<i>123)}) <-- Distribution: &Anonymous:Main([],{}) <-- Rule Application: &Anonymous:Main([],{}) <-- Distribution: &Anonymous:Root([],{}) <-- Rule Application: &Anonymous:Root([],{}) <== &Anonymous:Root([],{}) <== &Anonymous:Main([],{})
今回はパラメータをデフォルト値で実行しましたので、123から456まで37刻みで実行されます。lsコマンドで出力ファイルを確認します。
/Basic10$ ls process/output/ Even160.txt Even308.txt Even456.txt Odd197.txt Odd345.txt Even234.txt Even382.txt Odd123.txt Odd271.txt Odd419.txt
それぞれの値について偶数奇数の処理が実行されていることが確認できました。
基本11(ループ文2)
#基本10(ループ文)でループの作成方法を示しました。
しかし、#基本10(ループ文)の書き方では逐次処理となってしまうので、ループ数に比例して処理時間が掛かってしまいます。
この問題を解決する方法を考えましょう。
以下に示すのは、val=0がmax=64になるまで1を足し続け、{$val}.txtを作成する、ループを使った処理の例です。
以下に示していくファイルはこちらからダウンロードできます。
param $max := 64 Rule Main output '*.txt' Flow rule LoopSystem {val: 0} End Rule LoopSystem output '*.txt' param $val Flow rule Test {val: $val} if $val + 1 <= $max rule LoopSystem {val: $val + 1} end End Rule Test output '{$val}.txt' param $val Action touch {$O[1]} End
このままでは、#基本10(ループ文)同様、ループ部分は、逐次処理になっています。
そこで、ループの箇所を下記のような分割した形に書き換え、並列処理を試みます。
Rule LoopSystem output '*.txt' param $val Flow rule Test {val: $val} if ($val * 2) + 1 <= $max rule LoopSystem {val: ($val * 2) + 1} end if ($val * 2) + 2 <= $max rule LoopSystem {val: ($val * 2) + 2} end End
このようにすることでループに掛かる時間が改善されます。
あるいはconstraintを使用して下記のように記述する方法もあります。
Rule LoopSystem output '*.txt' param $val constraint $val <= $max Flow rule Test {val: $val} rule LoopSystem {val: ($val * 2) + 1} rule LoopSystem {val: ($val * 2) + 2} End
また、valの値を加算している箇所ですが、
rule LoopSystem {val: ($val * 2) + 1} rule LoopSystem {val: ($val * 2) + 2}
シーケンスを利用して下記のように書くこともできます。(参照:PIONEの式#シーケンス)
rule LoopSystem {val: ($val * 2) + (1|2)}
問題:これを踏まえて#基本10(ループ文)のループを改善してみましょう。
解答例1
Rule LoopSystem output '*.txt'.all param $val Flow if $val <= $maxval rule EvenOdd {val: $val} end if (($val * 2) - $minval) + $dval <= $maxval rule LoopSystem {val: (($val * 2) - $minval) + $dval} end if (($val * 2) - $minval) + ($dval * 2) <= $maxval rule LoopSystem {val: (($val * 2) - $minval) + ($dval * 2)} end End
Flow内の条件を分割しています。条件式が多少複雑になっていますので、演算の順番に注意しましょう。
また、現在のバージョンでの四則演算は二項演算のみの対応となっていますので、三項以上の場合は必ず括弧で括るようにして下さい(参照:PIONEの式#四則演算における注意事項)。
解答例2:constraintとシーケンスを利用した場合
Rule LoopSystem output '*.txt'.all param $val constraint $val <= $maxval Flow rule EvenOdd {val: $val} rule LoopSystem {val: (($val * 2) - $minval) + ($dval * (1|2))} End
これにより#基本10(ループ文)よりも呼び出されるルールの階層は浅くなります。
しかし、いずれも入れ子で呼び出されるのでルールが多くなるほど処理が重くなるでしょう。
そこで1つのルールから一気にループ数だけのルールを呼び出す方法を考えます。この記述方法については#基本15(ループ文3)をご覧下さい。
基本12(チケット)
PIONEの目的の一つは、できる限り並列処理を行うことですので、必ずしも記述の順番通りにルールが実行される訳ではありません。
ルールに順番を持たせたいときにはチケットという機能を利用します。但し、使い過ぎると待ち時間が多くなり並列計算できるPIONEの長所が活かせなくなってしまいますので注意が必要です。
下記の内容をご覧下さい。(CreateList-instant.pione)
このファイルはこちらからダウンロードできます。
Rule Main input '*.data'.all output 'list.txt' Flow rule Touch0 rule CreateList End Rule Touch0 output '0.data' Action sleep 1 touch {$O[1]} End Rule CreateList input '*.data'.all output 'list.txt' Action ls *.data > {$O[1]} End
これはCreateListで.dataファイルの一覧をlist.txtに記述する処理ですが、途中のTouch0でも0.dataファイルを作成しています。
この0.dataが一覧に含まれるかを検証してみましょう。
入力ファイル用のディレクトリ(input)内
/Basic12$ ls input/ 1.data 2.data
まずはMainルールを上記のようにそのまま記述している場合で実行してみます。
/Basic12$ pione-client CreateList-instant.pione -i input/ ==> &Anonymous:Root([.DS_Store,1.data,2.data],{}) --> Rule Application: &Anonymous:Root([.DS_Store,1.data,2.data],{}) --> Distribution: &Anonymous:Root([.DS_Store,1.data,2.data],{}) >>> &Anonymous:Main([1.data,2.data],{}) ==> &Anonymous:Main([1.data,2.data],{}) --> Rule Application: &Anonymous:Main([1.data,2.data],{}) --> Distribution: &Anonymous:Main([1.data,2.data],{}) >>> &Anonymous:Touch0([],{}) >>> &Anonymous:CreateList([1.data,2.data],{}) ==> &Anonymous:Touch0([],{}) SH ------------------------------------------------------------ SH sleep 1 SH touch 0.data SH ------------------------------------------------------------ <== &Anonymous:Touch0([],{}) ==> &Anonymous:CreateList([1.data,2.data],{}) SH ------------------------------------------------------------ SH ls *.data > list.txt SH ------------------------------------------------------------ <== &Anonymous:CreateList([1.data,2.data],{}) <-- Distribution: &Anonymous:Main([1.data,2.data],{}) <-- Rule Application: &Anonymous:Main([1.data,2.data],{}) <-- Distribution: &Anonymous:Root([.DS_Store,1.data,2.data],{}) <== &Anonymous:Main([1.data,2.data],{}) <-- Rule Application: &Anonymous:Root([.DS_Store,1.data,2.data],{}) <== &Anonymous:Root([.DS_Store,1.data,2.data],{})
では出力ファイルlist.txtを確認してみます。
/Basic12$ cat process/output/list.txt 1.data 2.data
0.dataが含まれませんでした。
これはTouch0とCreateListが同時に動き、Touch0が完了する前にCreateListが実行されているため、作成された0.dataがCreateListの入力ファイルとして登録されなかったからです。
では、チケットの機能を使ってMainルールを下記のようにしてみます。
Rule Main input '*.data'.all output 'list.txt' Flow rule Touch0 ==> <T> rule <T> ==> CreateList End
上記のようにチケット名は<>で囲んで、ルールとチケット間を==>で繋いで使用します。
チケット名を省略したい場合は下記のように記述します。
rule Touch0 >>> CreateList
下記のように連続させることも可能です。
rule First >>> Second >>> Third
また複数チケットの発行もできます。
rule Touch0 ==> (<T1>|<T2>) rule <T1> ==> CreateList rule <T2> ==> CreateList2
逆に複数チケットからのルール実行も可能です。
rule Touch0 ==> <T0> rule Touch1 ==> <T1> rule (<T0>|<T1>) ==> CreateList
上記の場合はチケットT0かつT1が発行されてからCreateListが実行されます。
では、実行してみます。
/Basic12$ pione-client CreateList-ticket.pione -i input/ ==> &Anonymous:Root([.DS_Store,1.data,2.data],{}) --> Rule Application: &Anonymous:Root([.DS_Store,1.data,2.data],{}) --> Distribution: &Anonymous:Root([.DS_Store,1.data,2.data],{}) >>> &Anonymous:Main([1.data,2.data],{}) ==> &Anonymous:Main([1.data,2.data],{}) --> Rule Application: &Anonymous:Main([1.data,2.data],{}) --> Distribution: &Anonymous:Main([1.data,2.data],{}) >>> &Anonymous:Touch0([],{}) ==> &Anonymous:Touch0([],{}) SH ------------------------------------------------------------ SH sleep 1 SH touch 0.data SH ------------------------------------------------------------ <== &Anonymous:Touch0([],{}) <-- Distribution: &Anonymous:Main([1.data,2.data],{}) --> Distribution: &Anonymous:Main([1.data,2.data],{}) >>> &Anonymous:CreateList([1.data,2.data,0.data],{}) ==> &Anonymous:CreateList([1.data,2.data,0.data],{}) SH ------------------------------------------------------------ SH ls *.data > list.txt SH ------------------------------------------------------------ <== &Anonymous:CreateList([1.data,2.data,0.data],{}) <-- Distribution: &Anonymous:Main([1.data,2.data],{}) <-- Rule Application: &Anonymous:Main([1.data,2.data],{}) <-- Distribution: &Anonymous:Root([.DS_Store,1.data,2.data],{}) <== &Anonymous:Main([1.data,2.data],{}) <-- Rule Application: &Anonymous:Root([.DS_Store,1.data,2.data],{}) <== &Anonymous:Root([.DS_Store,1.data,2.data],{}) /Basic12$ cat process/output/list.txt 0.data 1.data 2.data
今度は0.dataが含まれるようになりました。
基本13(orの使用例)
inputやoutputで宣言するときにorや|で繋ぐと、指定した複数種類のファイルからいずれかを入力や出力に使用することができます。
以下で使用するファイルはこちらからダウンロードできます。
入力ファイルでの使用例
Rule Main input '1.txt' or '2.txt' or '3.txt' output 'out.txt' Action cp {$I[1]} {$O[1]} End
上記では入力ファイルに1.txt, 2.txt, 3.txtのいずれかがあればout.txtにコピーする処理となっています。
例えば2.txtだけあった場合でも実行されます。
/Basic13$ pione-client test.pione -i ./input/ ==> &Anonymous:Root([.DS_Store,2.txt],{}) --> Rule Application: &Anonymous:Root([.DS_Store,2.txt],{}) --> Distribution: &Anonymous:Root([.DS_Store,2.txt],{}) >>> &Anonymous:Main([2.txt],{}) ==> &Anonymous:Main([2.txt],{}) SH ------------------------------------------------------------ SH cp 2.txt out.txt SH ------------------------------------------------------------ <== &Anonymous:Main([2.txt],{}) <-- Distribution: &Anonymous:Root([.DS_Store,2.txt],{}) <-- Rule Application: &Anonymous:Root([.DS_Store,2.txt],{}) <== &Anonymous:Root([.DS_Store,2.txt],{})
上記の使用例は入力ファイルが1つだけあった場合のみに対応しています。
複数のファイルがあった場合、複数ファイルで上書きコピーされてしまいます。
出力ファイルでの使用例
param $Mode := 1 Rule Main output 'out1.txt' or 'out2.txt' or 'err.txt' Action if [ {$Mode} -eq 1 ] ; then echo "Mode: {$Mode}" > {$O[1].nth(1)} elif [ {$Mode} -eq 2 ] ;then echo "Mode: {$Mode}" > {$O[1].nth(2)} else echo "Error: Mode {$Mode} is not Supported!" > {$O[1].nth(3)} fi End
これはパラメータModeが1のときはout1.txtを作成し、2のときはout2.txtを作成し、それ以外についてはエラーを残す処理となっています。
out1.txt, out2.txt, err.txtのいずれかが出力されることで処理が完了しますので、状況に応じて作成するファイルを変えたいときに有効です。
.nth()は引数で指定した要素を取り出すシーケンスのメソッドです。(参照:PIONEの式#シーケンス)
デフォルト(Mode=1)で実行してみます。
/Basic13$ pione-client OutputOr.pione ==> &Anonymous:Root([],{}) --> Rule Application: &Anonymous:Root([],{}) --> Distribution: &Anonymous:Root([],{}) >>> &Anonymous:Main([],{}) ==> &Anonymous:Main([],{}) SH ------------------------------------------------------------ SH if [ 1 -eq 1 ] ; then SH echo "Mode: 1" > out1.txt SH elif [ 1 -eq 2 ] ;then SH echo "Mode: 1" > out2.txt SH else SH echo "Error: Mode 1 is not Supported!" > err.txt SH fi SH ------------------------------------------------------------ <-- Distribution: &Anonymous:Root([],{}) <-- Rule Application: &Anonymous:Root([],{}) <== &Anonymous:Root([],{}) <== &Anonymous:Main([],{}) /Basic13$ cat process/output/out1.txt Mode: 1
out1.txtが作成されて完了します。
Mode=0で実行してみます。
/Basic13$ pione-client OutputOr.pione --params={Mode:0} "{Mode:0}" ==> &Anonymous:Root([],{}) --> Rule Application: &Anonymous:Root([],{}) --> Distribution: &Anonymous:Root([],{}) >>> &Anonymous:Main([],{}) ==> &Anonymous:Main([],{}) SH ------------------------------------------------------------ SH if [ 0 -eq 1 ] ; then SH echo "Mode: 0" > out1.txt SH elif [ 0 -eq 2 ] ;then SH echo "Mode: 0" > out2.txt SH else SH echo "Error: Mode 0 is not Supported!" > err.txt SH fi SH ------------------------------------------------------------ <-- Distribution: &Anonymous:Root([],{}) <== &Anonymous:Main([],{}) <-- Rule Application: &Anonymous:Root([],{}) <== &Anonymous:Root([],{}) /Basic13$ cat process/output/err.txt Error: Mode 0 is not Supported!
err.txtが作成されて完了します。
基本14(例外)
PIONEでも入出力ファイルにワイルドカード*を使用して全ての該当ファイルを対象とできますが、exceptを使用することにより対象外とするファイルを指定できます。メソッドexcept, exceptionsについてはPIONEの式#データ表現型を参照して下さい。
Rule Main input 'AllData.txt' output '*.txt'.all.except($I[1]) Flow rule GetLine {filename:"Data1", line:1} rule GetLine {filename:"Data2", line:2} rule GetLine {filename:"Data3", line:3} End Rule GetLine input 'AllData.txt' output '{$filename}.txt' param $filename param $line Action head -{$line} {$I[1]} | tail -1 > {$O[1]} End
上記はAllData.txtから1, 2, 3行目を指定した.txtファイルに出力する処理です。出力ファイルは全ての.txtとしていますが、exceptによってAllData.txtを対象外としています。対象外としていない場合はAllData.txtが出力ファイルの条件を満たしていますので、発火しません。
このファイルはこちらからダウンロードできます。
AllData.txtを下記の内容として実行してみます。
ABC DEF GHI
実行結果
/Basic14$ pione-client DivideText.pione -i DivideTextInput/ ==> &Anonymous:Root([.DS_Store,AllData.txt],{}) --> Rule Application: &Anonymous:Root([.DS_Store,AllData.txt],{}) --> Distribution: &Anonymous:Root([.DS_Store,AllData.txt],{}) >>> &Anonymous:Main([AllData.txt],{}) ==> &Anonymous:Main([AllData.txt],{}) --> Rule Application: &Anonymous:Main([AllData.txt],{}) --> Distribution: &Anonymous:Main([AllData.txt],{}) >>> &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data1"),line:(<i>1)}) >>> &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data2"),line:(<i>2)}) ==> &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data1"),line:(<i>1)}) SH ------------------------------------------------------------ SH head -1 AllData.txt | tail -1 > Data1.txt SH ------------------------------------------------------------ >>> &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data3"),line:(<i>3)}) <== &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data1"),line:(<i>1)}) ==> &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data2"),line:(<i>2)}) SH ------------------------------------------------------------ SH head -2 AllData.txt | tail -1 > Data2.txt SH ------------------------------------------------------------ <== &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data2"),line:(<i>2)}) ==> &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data3"),line:(<i>3)}) SH ------------------------------------------------------------ SH head -3 AllData.txt | tail -1 > Data3.txt SH ------------------------------------------------------------ <== &Anonymous:GetLine([AllData.txt],{filename:(<s>"Data3"),line:(<i>3)}) <-- Distribution: &Anonymous:Main([AllData.txt],{}) <-- Rule Application: &Anonymous:Main([AllData.txt],{}) <-- Distribution: &Anonymous:Root([.DS_Store,AllData.txt],{}) <== &Anonymous:Main([AllData.txt],{}) <-- Rule Application: &Anonymous:Root([.DS_Store,AllData.txt],{}) <== &Anonymous:Root([.DS_Store,AllData.txt],{}) /Basic14$ cat process/output/Data1.txt ABC /Basic14$ cat process/output/Data2.txt DEF /Basic14$ cat process/output/Data3.txt GHI
1, 2, 3行目の内容がそれぞれのファイルに出力されました。
.except($I[1])を除いた場合は発火しません。
/Basic14$ pione-client DivideText.pione -i DivideTextInput/ ==> &Anonymous:Root([.DS_Store,AllData.txt],{}) --> Rule Application: &Anonymous:Root([.DS_Store,AllData.txt],{}) <-- Rule Application: &Anonymous:Root([.DS_Store,AllData.txt],{}) <== &Anonymous:Root([.DS_Store,AllData.txt],{})
また、exceptionsを使用すると設定している例外をリストで得ることができます。
Rule Main input '1.txt' input '2.txt' input '3.txt' output '*.txt'.all.except($I[1] or $I[2] or $I[3]) Action for data in {$O[1].exceptions()} do cat $data >> AllData.txt wc $data >> AllInfo.txt done End
上記の例ではoutputに例外が$I[1] or $I[2] or $I[3]と設定されているので、{$O[1].exceptions()}では1.txt 2.txt 3.txtのリストとして得られるので、for文がこれらのファイルによって回ることになります。
基本15(ループ文3)
#基本10(ループ文)、#基本11(ループ文2)にてループ文を記述する方法を示しました。しかし、いずれも多くのルールを入れ子で呼ぶことになり、ループ数が多くなると入出力ファイルの引き継ぎも多くなり時間が掛かってしまいます。今回は一つのルールから全てのルールを呼び出すようにしてループを行うようにしてみます。
下記の整数型メソッドuptoを使用すると、1つのルールからパラメータを一気に設定して各々の値でルールを実行することができます。uptoは引数の値まで1ずつ加算しながら整数を返すメソッドです。(参照:PIONEの式#整数型(integer))
param $min := 0 param $max := 64 Rule Main output '*.txt' Flow rule Test {val: $min.upto($max)} End Rule Test output '{$val}.txt' param $val Action touch {$O[1]} End
またdowntoを使用する方法もあります。uptoは引数の値まで1ずつ減算しながら整数を返すメソッドです。(参照:PIONEの式#整数型(integer))
rule Test {val: $max.downto($min)}
uptoを利用すると、#基本10(ループ文)は下記のようになります。
param $maxval := 456 param $minval := 123 param $dval := 37 Rule Main output '*.txt'.all Flow rule EvenOdd {val: $minval + ($dval * (0.upto(($maxval - $minval)/$dval)))} End Rule EvenOdd output '*.txt' param $val Flow if $val % 2 == 0 rule Even {val: $val} else rule Odd {val: $val} end End Rule Even output '*.txt' param $val Action echo "{$val} is even." >> Even{$val}.txt; End Rule Odd output '*.txt' Action echo "{$val} is odd." >> Odd{$val}.txt; Endこのファイルはこちらからダウンロードできます。
valに設定する式が複雑な場合は下記のように一旦、変数束縛で計算したものを利用する方法もあります。
param $maxval := 456 param $minval := 123 param $dval := 37 $loop := ($maxval - $minval)/$dval Rule Main output '*.txt'.all Flow rule EvenOdd {val: $minval + ($dval * (0.upto($loop))} End -以下略-
上記の場合は変数loopにパラメータから計算したループ数を設定しています。
基本16(ランダム)
メソッドrandomを使用するとランダムに選んだファイルを出力することができます。(現v0.5.0では未実装)
Rule Main input '*.txt' output '*.out'.all.random Action max=$(wc -l {$I[1]} | awk '{print $1}') for (( i=1; i<${max}; i++ )) do head -${i} {$I[1]} | tail -1 > {$I[1][1]}-{$i}.out done End
上記の例では入力.txtファイルからランダムに選んだ行数を.outファイルとして出力します。このファイルはこちらからダウンロードできます。
応用
さて、ここまでの基本を組み合わせて、Eosのコマンドを使用する問題を考えてみましょう。
応用1(フローの制御)
問題1
但し、*.parametersは以下のフォーマットで切り出しサイズ(Sx, Sy, Sz)を格納しているファイルとします。
Sx Sy Sz
応用問題1:上記のCenterGet.pioneを作成してみましょう。
解答例
Rule Main input '*.mrc'.all output '*.roi' Flow rule First rule Second End Rule First input '*.mrc' output '{$I[1]}.info' Action mrcImageInfo -I -i {$I[1]} \ | head -2 | tail -1 \ | awk '{printf("%s %s %s", $3, $4, $5)}' \ | tr -c '[0-9]' ' ' \ > {$O[1]} End Rule Second input '*.mrc' input '{$I[1]}.info' input '{$I[1][1]}.parameters' output '{$I[1][1]}.roi' Action Center_x=$(awk '{printf("%s\n", $1)}' {$I[2]}) Center_y=$(awk '{printf("%s\n", $2)}' {$I[2]}) Center_z=$(awk '{printf("%s\n", $3)}' {$I[2]}) N_x=$(awk '{printf("%s\n", $1)}' {$I[3]}) N_y=$(awk '{printf("%s\n", $2)}' {$I[3]}) N_z=$(awk '{printf("%s\n", $3)}' {$I[3]}) mrcImageCenterGet -i {$I[1]} -o {$O[1]} \ -Cx $Center_x -Cy $Center_y -Cz $Center_z \ -Nx $N_x -Ny $N_y -Nz $N_z End
このファイルはこちらからダウンロードできます。
主にmrcImageCenterGetを用いて切り出しを行っています。
Main: .mrcファイルについてFirst, Secondの処理を行います。 First: .mrcファイルの最大値の座標を得て、.mrc.infoファイルに格納します。 Second: .mrcから.mrc.infoの座標を中心に.parametersのサイズで切り出しを行います。
実行結果
入力ファイル
mrcファイル1
Min: 0 (0, 0, 0) Max: 3398.12 (23, 55, 41) Mean: 72.129 SD: 294.805 SE: 0.368507 Sum: 4.61626e+07
最大値の座標: (23, 55, 41)
Min: -2390.42 (77, 14, 69) Max: 4374.62 (17, 62, 46) Mean: 72.1487 SD: 577.82 SE: 0.722275 Sum: 4.61752e+07
最大値の座標: (17, 62, 46)
parametersファイル1, 2の両方を下記に設定して実行してみます。
40 40 50
コマンド
pione-client CenterGet.pione -i CenterGetInput/ -b CenterGet
実行結果
1VOM.roi, 1VOM-N.roiのファイルが出力されます。
mrcImageInfoを使って、最大値の座標が中心になっているか確認してみます。
/Advanced1$ mrcImageInfo -I -i CenterGet/output/1VOM.roi Min: 0 (0, 0, 0) Max: 3398.12 (19, 19, 24) Mean: 241.805 SD: 500.988 SE: 1.77126 Sum: 1.93444e+07 /Advanced1$ mrcImageInfo -I -i CenterGet/output/1VOM-N.roi Min: -1893.8 (0, 4, 33) Max: 4374.62 (19, 19, 24) Mean: 157.55 SD: 648.124 SE: 2.29147 Sum: 1.2604e+07
最大値を中心に切り出していることが分かります。
補足(ファイルの確認用コマンド)
出力したファイルと入力ファイルの違いを見るために後処理を追加します。
作成例
Rule Main input '*.mrc'.all output '*.roi' output '*.tiff' Flow rule First rule Second rule Final End -中略- Rule Final input '*.mrc' input '{$I[1][1]}.roi' output '{$I[1]}-1.tiff' output '{$I[2]}-1.tiff' output '{$I[1]}-2.tiff' output '{$I[2]}-2.tiff' Action mrcImageProjection -i {$I[1]} -o {$I[1]}.2d mrc2tiff -i {$I[1]}.2d -o {$O[1]} mrcImageProjection -i {$I[1]} -o {$I[1]}.2d1 -m 1 mrc2tiff -i {$I[1]}.2d1 -o {$O[3]} mrcImageProjection -i {$I[2]} -o {$I[2]}.2d mrc2tiff -i {$I[2]}.2d -o {$O[2]} mrcImageProjection -i {$I[2]} -o {$I[2]}.2d1 -m 1 mrc2tiff -i {$I[2]}.2d1 -o {$O[4]} End
これにより入力ファイル(mrc), 出力ファイル(roi)についてxy平面とyz平面へ投影した画像をtiffファイルで出力します。
出力ファイル
roiファイル1
roiファイル2
後処理をしたくないときはMainのoutputとFlow内をコメントアウトするだけです。
Rule Main input '*.mrc'.all output '*.roi' # output '*.tiff' Flow rule Initial rule First rule Second # rule Final End -後略-
補足(デフォルトサイズの設定)
.parametersなどのファイルがない場合にパラメータ入力で設定したい場合の前処理を追加します。
作成例
Basic Param $Nx := 40 $Ny := 40 $Nz := 50 End Rule Main input '*.mrc'.all input '*.parameters' output '*.roi' output '*.tiff' Flow rule Initial rule First rule Second rule Final End Rule Initial input '*.mrc' output '{$I[1][1]}.parameters' Action echo "{$Nx} {$Ny} {$Nz}" > {$O[1]} End -後略-
このようにすると、.parametersがない場合にはオプション--paramsまたはデフォルト値(40, 40, 50)によってサイズを設定することができます。但し、入力ファイルに.parametersが1つもない場合は動きません。その場合はtouchなどを使用して.parameterを1つは用意する必要があります。
1VOM.parametersのみ下記のように設定してみます。
64 64 64
Nx=80のみをパラメータとしてコマンド入力してみます。
/Advanced1$ pione-client CenterGet.pione -i ${EOS_HOME}/tutorial/SampleData/ -b CenterGet --params="{Nx:80}"
出力ファイル
1VOM.roiはファイルに従ったサイズになっています。
1VOM-N.roiはNxがコマンドの引数、Ny, Nzはデフォルト値のパラメータによるサイズになります。
応用2(ウェブページでパラメータを設定しながらコマンドを実行)
次は#基本8(インタラクティブ操作)のようにウェブページの操作を行い、Eosのコマンドを実行する処理を作ってみましょう。
問題2
2Dファイル(mrcImage)を入力ファイルとし、ローパスフィルタ(mrcImageLowPassFilter)をウェブページの操作によって実行する次のコマンドを考えます。
- 入力ファイルはファイル選択画面から自由に選べるようにし、オプションの値も画面操作で設定できるようにします。
- コマンド実行後は入力ファイルと出力ファイルの画像ファイルをそれぞれ表示して比較できるようにします。
- この画面を見ながら再設定するか、終了するかを選び、終了後に最後の変換ファイルを出力ファイルとして得られるようにします。
応用問題2:上記の機能を持つパッケージLowPassFilter.ppgを作成してみましょう。
解答例
PIONE定義書、htmlファイル、cgiファイルをそれぞれ作成します。
PIONE定義書
メイン(Main.pione)の処理を定義します。今回は設定のほとんどをウェブページに委ねるのでここでは出力ファイル名のみ定義しています。
Rule Main output '*.lpf'.all Flow rule Interaction End
インタラクティブ(Interaction.pione)の処理を定義します。このファイルは再利用できるように拡張子が付いた全てのファイルを呼び出し元に出力できるようにしています。
Rule Interaction output '*.*'.all Action # build public directory for pione-interactive mkdir public cp etc/* public cp bin/* public # start interactive operation pione-interactive browser --public public cp public/* . End
パッケージ用のアノテーション(Annotation.pione)を定義します。
.@ PackageName :: "LowPassFilter" .@ Editor :: "Kinoshita" .@ Tag :: "v0.1.0"
htmlファイル
Index.htmlはコマンド選択画面にしています。ここから実行したいコマンドを選択します。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>InteractiveCommand Index Page</title> </head> <body> <a href="mrcImageLowPassFilter.html">mrcImageLowPassFilter</a> <br> <br> <a href="?pione-action=finish">終了</a> </body> </html>
mrcImageLowPassFilter.htmlでコマンドや入力ファイルなどのオプションを設定できるようにします。開始ボタンでmrcImageLowPassFilterが実行されます。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>mrcImageLowPassFilter</title> </head> <body> <form action="mrcImageLowPassFilter.cgi" method="post" enctype="multipart/form-data"> <button type="submit">開始</button> <br> <table> <tr> <td> <input type="file" name="InputFile"/> </td> <td> 入力ファイル名(-i) </td> </tr> <tr> <td> <input type="text" name="OutputFile" value="outdata.lpf"/> </td> <td> 出力ファイル名(-o) </td> </tr> <tr> <td> <input type="text" name="HalfValuePoint" value="1.0"/> </td> <td> HalfValuePoint[A-1] (強度を半分に落とす空間周波数を示す)(-hvp) </td> </tr> <tr> <td> <input type="text" name="Width" value="1.0"/> </td> <td> 空間周波数のcos関数の幅を設定(-w) </td> </tr> <tr> <td> <select name="Mode"> <option value="1">1: ステップフィルタ</option> <option value="2">2: cosフィルタ</option> <option value="3">3: expフィルタ</option> <option value="4">4: ガウシアンフィルタ</option> <option value="5">5: ローレンツ型フィルタ</option> </select> </td> <td> モード(-m) </td> </tr> </table> </form> <br> <br> <a href="?pione-action=finish">終了</a> </body> </html>
cgiファイル
mrcImageLowPassFilter.cgi
#!/usr/bin/env ruby require 'cgi' cgi = CGI.new ### Header HTMLstr = "" HTMLstr += "<!DOCTYPE html>" HTMLstr += "<html>" HTMLstr += "<head>" HTMLstr += "<title>mrcImageLowPassFilter</title>" HTMLstr += "</head>" HTMLstr += "<body>" ### Close or Back HTMLstr += '<a href="?pione-action=finish">Close</a>' HTMLstr += "<br>" HTMLstr += '<a href="mrcImageLowPassFilter.html">Back</a>' HTMLstr += "<br>" HTMLstr += "<br>" # Main Process ## Query to Parameters ### Copy InputFile fpQueryInputFile = cgi.params['InputFile'][0] strInputFile = fpQueryInputFile.original_filename fpInputFile = open(strInputFile, "wb") fpInputFile.write(fpQueryInputFile.read) fpInputFile.close ### Other Query strOutputFile = cgi['OutputFile'] strHalfValuePoint = cgi['HalfValuePoint'] strWecth = cgi['Width'] strMode = cgi['Mode'] ### mrcImageLowPassFilter command = "mrcImageLowPassFilter" command += ' -i "' + strInputFile + '"' command += ' -o "' + strOutputFile + '"' command += ' -hvp "' + strHalfValuePoint + '"' command += ' -w "' + strWecth + '"' command += ' -m "' + strMode + '"' system(command) ### mrc2gif #### For input strGifInputFile = strInputFile + ".gif" command = "mrc2gif" command += ' -i "' + strInputFile + '"' command += ' -o "' + strGifInputFile + '"' system(command) #### For output strGifOutputFile = strOutputFile + ".gif" command = "mrc2gif" command += ' -i "' + strOutputFile + '"' command += ' -o "' + strGifOutputFile + '"' system(command) ### mrcImageInfo #### For input strInfoInputFile = strInputFile + ".info" command = "mrcImageInfo" command += ' -I' command += ' -i "' + strInputFile + '"' command += ' -o "' + strInfoInputFile + '"' system(command) #### For output strInfoOutputFile = strOutputFile + ".info" command = "mrcImageInfo" command += ' -I' command += ' -i "' + strOutputFile + '"' command += ' -o "' + strInfoOutputFile + '"' system(command) ## View as HTML Statement ### Table HTMLstr += "<table>" #### Title HTMLstr += "<tr>" HTMLstr += "<td>" + strInputFile + "</td>" HTMLstr += "<td>" + strOutputFile + "</td>" HTMLstr += "</tr>" #### GifImage ##### For Input HTMLstr += "<tr>" HTMLstr += "<td>" HTMLstr += '<img src="' + strGifInputFile + '">' HTMLstr += "</td>" ##### For Output HTMLstr += "<td>" HTMLstr += '<img src="' + strGifOutputFile + '">' HTMLstr += "</td>" HTMLstr += "</tr>" #### mrcImageInfo ##### For Input HTMLstr += "<tr>" HTMLstr += "<td>" fpInfoInputFile = open(strInfoInputFile, "r") fpInfoInputFile.each do |line| HTMLstr += line + "<br>" end fpInfoInputFile.close HTMLstr += "</td>" ##### For Output HTMLstr += "<td>" fpInfoOutputFile = open(strInfoOutputFile, "r") fpInfoOutputFile.each do |line| HTMLstr += line + "<br>" end fpInfoOutputFile.close HTMLstr += "</td>" HTMLstr += "</tr>" HTMLstr += "</table>" HTMLstr += "</body>" HTMLstr += "</html>" # Output as html cgi.out(type: "text/html") do HTMLstr end
htmlヘッダなどの書き込み、クエリを変数やファイルに変換、Eosのコマンド実行、実行結果をhtml化の順に記述しています。このコードではmrcImageLowPassFilterの結果をgif画像とmrcImageInfo(-I)の情報を表示して比較できるようにしています。
これらのファイルはこちらからダウンロードできます。
パッケージ作成
コードが完成したらパッケージ作成を行います。
/PIONE$ pione package build Advanced2 info: update the package info file: local:/Eos/tutorial/SampleCode/PIONE/Advanced2/pione-package.json info: Package local:/Eos/tutorial/SampleCode/PIONE/LowPassFilter(Kinoshita)+v0.1.0.ppg has been built successfully.
実行結果
では、動作させてみましょう。
PIONE Webclientのジョブページで実行し、Interactionを選択してindexページを開きます。ここでmrcImageLowPassFilterを選択すると設定画面が表示されます。ここでは入力ファイルや設定を変更しながらコマンドの実行を試すことができます。終了やCloseを選択すると、最後に処理を行った結果で出力ファイルが残ります。また、出力ファイル名を変更すると複数のファイルを残すことができます。
入力ファイル(2D)
最小 最大 |
-18651.7 (10, 1, 0) 52942.7 (24, 39, 0) |
実行例1 | ||
> | ||
実行例2 | ||
> | ||
実行例3 | ||
> |
終了後にResult Filesからファイルをダウンロードすると、outputディレクトリにて最後に変換した.lpfファイルを得ることができます。あるいはこのPIONE定義書に処理を追加して、作成した.lpfファイルを入力ファイルとして使用することもできます。
補足(不要ファイルの削除)
今回の処理は操作によって自由にファイルが作成できます。しかし、中には不要なファイルも出てくる場合もあります。そこで、補足処理としてサーバ上のファイルリストを表示し、不要ファイルを削除できるようにしてみましょう。新しく下記のファイルを追加します。なお、pione-action=...についてはインタラクションAPIに詳細を記載しています。
/etc/FileDelete.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>InteractiveCommand Index Page</title> </head> <body> <a href="index.html">戻る</a><br> <a href="?pione-action=finish">終了</a><br> <br> <form action="FileDelete.cgi" method="post" enctype="multipart/form-data"> 削除ファイル名<br> <input type="text" name="DeleteFile" value=""/> <button type="submit">削除</button> </form> ファイルリスト<br> <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script> <div id="textDiv"></div> <script type="text/javascript"> var div = document.getElementById("textDiv"); div.textContent = ""; $.getJSON("./", {"pione-action": "list"}, function(data){ $.each(data, function() { strOut = ".lpf" if( this.name.indexOf(strOut) + strOut.length == this.name.length ){ div.textContent += this.name +"\n"; } }); }); </script> </body> </html>
ファイルリストではpione-action=listによって得たファイルリストの中から最後が.lpfで終わるファイルのみを出力するようにしています。このリストを見ながら削除するファイルをテキストボックスに書き込んで、削除ボタンを押すと削除処理(FileDelete.cgi)が行われます。
/bin/FileDelete.cgi
#!/usr/bin/env ruby require 'cgi' cgi = CGI.new ### Header HTMLstr = "" HTMLstr += "<!DOCTYPE html>" HTMLstr += "<html>" HTMLstr += "<head>" HTMLstr += "<title>FileDelete</title>" ## Query to Parameters strDeleteFile = cgi['DeleteFile'] ## View as HTML Statement HTMLstr += '<meta http-equiv="refresh" content="0;URL=./' + strDeleteFile + '?pione-action=delete">' HTMLstr += "</head>" HTMLstr += "</html>" # Output as html cgi.out(type: "text/html") do HTMLstr end
ヘッダにてDeleteFileを削除する操作(pione-action=delete)を行っています。
さらに出来上がったページへアクセスするためにindex.htmlのbody内に下記の内容を追加します。
<a href="FileDelete.html">FileDelete</a><br>
これにより不要ファイルの削除を行うことができます。
上記の例ではOutdata.lpfを削除しています。
削除完了画面が表示されるので、ブラウザのバックボタンで戻ります。
画面表示を更新するとOutdata.lpfが消えていることが確認できます。また、インタラクティブ操作を終了し、ファイルをダウンロードするとOudata.lpfが無いことも確認できます。これにより不要ファイルを削除できたことが分かりました。
応用3(参照像の作成)
今回はループ文を使用した例を示します。
問題3
入力ファイルを参照用の3次元画像(.ref3d)として、指定した範囲の角度で参照像(.ref2d)を作成する処理を考えます。PIONE Webclientを利用し、作成した各参照像の画像を確認しながら、ユーザ操作によって適切な角度を決定できるようにします。
応用問題3:上記の機能を持つパッケージRef3DtoRef2D.ppgを作成してみましょう。
解答例
PIONE定義書
ユーザ操作によって3次元画像と投影する角度を設定し、そこから参照像を作成して、結果を画像として確認し、決定かやり直しかを選択できる流れとしています。
Annotation.pione
.@ PackageName :: "Ref3DtoRef2D" .@ Editor :: "Kinoshita" .@ Tag :: "v0.2.0"
Main.pione
Rule Main output '*.ref3d' output '*.ref2d' output '*.mon' output '*.gif' Flow rule Start rule SubMain rule Result rule Finish End Rule Start output 'Start0!Flag' Action touch {$O[1]} End Rule SubMain input 'Start*!Flag' output '*.*'.all Flow rule Interaction {val : '{$I[1][1]}'.str().i()} rule Ref3DtoRef2D {val : '{$I[1][1]}'.str().i()} End Rule Finish input 'Finish*!Flag' input '*.*-{$I[1][1]}' output '{$I[2][1]}.{$I[2][2]}' Action cp {$I[2]} {$O[1]} End
SubMainルールにて角度と3次元画像の設定とその2次元画像の作成を実行します。このルールはStartN!Flagで発火し、最初はStartルールで、やり直すときはResultルールでそれぞれStartN!Flagを作成します。このときのNはやり直しのために設定した整数で、前に作成されたファイルとこれから作成するファイルが混ざらないようにするために設定しています。ルールFinishで決定したときのファイルのみを取り出します。
Interaction.pione
Rule Interaction input 'Start{$val}!Flag' output '*.*'.all param $val Action # build public directory for pione-interactive mkdir public cp etc/* public cp bin/* public # start interactive operation pione-interactive browser --public public cp public/* . for file in $(ls *Mode *Rot1 *Rot2 *Rot3 *.ref3d) ; do mv "$file" "$file-{$val}"; done End
やり直しに対応できるように処理の最後で作成されたファイルに番号を付けています。
Ref3DtoRef2D.pione
Rule Ref3DtoRef2D input '*!*!*.!Rot1-{$val}' input '*!*!*.!Rot2-{$val}' input '*!*!*.!Rot3-{$val}' input '*.!Mode-{$val}' input '*.ref3d-{$val}' output '*.ref2d-{$val}'.all output 'Ref3DtoRef2D.info-{$val}' $min1 := '{$I[1][1]}'.str().f() $max1 := '{$I[1][2]}'.str().f() $delta1 := '{$I[1][3]}'.str().f() $loop1 := (($max1 - $min1) / $delta1).i() $min2 := '{$I[2][1]}'.str().f() $max2 := '{$I[2][2]}'.str().f() $delta2 := '{$I[2][3]}'.str().f() $loop2 := (($max2 - $min2) / $delta2).i() $min3 := '{$I[3][1]}'.str().f() $max3 := '{$I[3][2]}'.str().f() $delta3 := '{$I[3][3]}'.str().f() $loop3 := (($max3 - $min3) / $delta3).i() $mode := '{$I[4][1]}'.str() param $val Flow rule One3Dto2D {rot1: $min1 + ((0.upto($loop1)).f() * $delta1), rot2: $min2 + ((0.upto($loop2)).f() * $delta2), rot3: $min3 + ((0.upto($loop3)).f() * $delta3), mode : $mode, val : $val} rule Info3Dto2D {val : $val} End Rule Info3Dto2D input '*.ref2d-{$val}'.all output 'Ref3DtoRef2D.info-{$val}' param $val Action for file in $(ls *.ref2d-{$val}) do echo "./$file" >> {$O[1]} done End Rule One3Dto2D input '*.ref3d-{$val}' output '*.ref2d-{$val}'.all param $rot1 param $rot2 param $rot3 param $mode param $val Action output="{$I[1][1]}-{$mode}-{$rot1}-{$rot2}-{$rot3}.ref2d-{$val}" mrc3Dto2D -i {$I[1]} -o $output \ -Rot1 {$rot1} {$rot1} 1 \ -Rot2 {$rot2} {$rot2} 1 \ -Rot3 {$rot3} {$rot3} 1 \ -EulerMode {$mode}; End
ルールRef3DtoRef2Dは大きくFlowLoop3Dto2D1とOne3Dto2Dの2つのルールに分かれます。ここでもやり直しに対応するためにファイルの末尾に番号を付けます。ルールInfo3Dto2Dでは画像作成のために2次元画像の一覧を作成します。ルールOne3Dto2D1ではuptoによるループを利用して3次元画像からmrc3Dto2Dによって2次元画像を次々に作成します。このとき3軸毎に回転角度をそれぞれuptoで設定しています。
Result.pione
Rule Result input 'Ref3DtoRef2D.info-*' input '*.ref2d-{$I[1][1]}'.all output '*!Flag' output 'Ref3DtoRef2D.mon-{$I[1][1]}' output 'Ref3DtoRef2D.gif-{$I[1][1]}' Flow rule Ref2DtoGIF {val : '{$I[1][1]}'.str().i()} rule ResultCheck {val : '{$I[1][1]}'.str().i()} End Rule Ref2DtoGIF input 'Ref3DtoRef2D.info-{$val}' input '*.ref2d-{$val}'.all output 'Ref3DtoRef2D.mon-{$val}' output 'Ref3DtoRef2D.gif-{$val}' param $val Action mrcImageMontageCreate -i {$I[1]} -o {$O[1]} mrc2gif -i {$O[1]} -o {$O[2]} End Rule ResultCheck input 'Ref3DtoRef2D.gif-{$val}' output '*!Flag' param $val Action # build public directory for pione-interactive mkdir public cp etc/index2.html public/index.html cp bin/* public cp {$I[1]} public/Ref3DtoRef2D.gif # start interactive operation pione-interactive browser --public public cp public/* . if [ -e "Finish!Flag" ]; then mv "Finish!Flag" "Finish{$val}!Flag"; elif [ -e "Start!Flag" ]; then mv "Start!Flag" "Start{$val + 1}!Flag"; fi End
ルールResultはRef2DtoGIFとResultCheckに分かれます。ルールRef2DtoGIFでは各2次元像をひとまとめにしたgif画像を作成します。ルールResultCheckではgif画像をindex2.htmlのページに表示して、ユーザ操作にて決定のときはFinishN!Flagをやり直しのときはStartN!Flagを作成するようにしています。
htmlファイル
index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>InteractiveCommand Index Page</title> </head> <body> <a href="Ref3DtoRef2D.html">Ref3DtoRef2D</a><br> <br> <a href="?pione-action=finish">終了</a> </body> </html>
Ref3DtoRef2D.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Ref3DtoRef2D</title> </head> <body> <form action="Ref3DtoRef2D.cgi" method="post" enctype="multipart/form-data"> <button type="submit">開始</button> <br> <table> <tr> <td> <input type="file" name="InputFile"/> </td> <td> 入力ファイル名(.ref3d) </td> </tr> <tr> <td> <select name="Mode1"> <option value="X">X</option> <option value="Y">Y</option> <option value="Z">Z</option> </select> <select name="Mode2"> <option value="O">O</option> <option value="E">E</option> </select> <select name="Mode3"> <option value="Y">Y</option> <option value="N">N</option> </select> <select name="Mode4"> <option value="S">S</option> <option value="R">R</option> </select> </td> <td> 回転モード </td> </tr> </table> <table> <tr> <td> </td> <td> 最小値 </td> <td> 最大値 </td> <td> 加算値 </td> </tr> <tr> <td> 角度1 </td> <td> <input type="text" name="RotMin1" value="0"/> </td> <td> <input type="text" name="RotMax1" value="359"/> </td> <td> <input type="text" name="RotDelta1" value="30"/> </td> </tr> <tr> <td> 角度2 </td> <td> <input type="text" name="RotMin2" value="0"/> </td> <td> <input type="text" name="RotMax2" value="359"/> </td> <td> <input type="text" name="RotDelta2" value="30"/> </td> </tr> <tr> <td> 角度3 </td> <td> <input type="text" name="RotMin3" value="0"/> </td> <td> <input type="text" name="RotMax3" value="0"/> </td> <td> <input type="text" name="RotDelta3" value="30"/> </td> </tr> </table> </form> <br> <br> <a href="index.html">戻る</a><br> <a href="?pione-action=finish">終了</a><br> </body> </html>
このページでは入力ファイルと回転モードと及び各角度毎の最小値、最大値、加算値を設定して実行できるようにしています。
index2.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>InteractiveCommand Index Page</title> </head> <body> <img src="Ref3DtoRef2D.gif"><br> <br> <form action="Result.cgi" method="post" enctype="multipart/form-data"> <button type="submit">決定</button><br> <input type="hidden" name="Result" value="Finish"/> </form> <form action="Result.cgi" method="post" enctype="multipart/form-data"> <button type="submit">やり直し</button><br> <input type="hidden" name="Result" value="Start"/> </form> </body> </html>
これはルールResultのときに画像を確認しながら、決定かやり直しかを選択するためのページです。
cgiファイル
Ref3DtoRef2D.cgi
#!/usr/bin/env ruby require 'cgi' cgi = CGI.new ### Header strHTML = "" strHTML += "<!DOCTYPE html>" strHTML += "<html>" strHTML += "<head>" strHTML += "<title>Ref3DtoRef2D</title>" ### Auto Close strHTML += '<meta http-equiv="REFRESH" content="0;URL=?pione-action=finish">' # Main Process ## Query to Parameters ### Copy InputFile fpQueryInputFile = cgi.params['InputFile'][0] strInputFile = fpQueryInputFile.original_filename fpInputFile = open(strInputFile, "wb") fpInputFile.write(fpQueryInputFile.read) fpInputFile.close ### Other Query strMode = cgi['Mode1'] + cgi['Mode2'] + cgi['Mode3'] + cgi['Mode4'] strRotMin1 = cgi['RotMin1'] strRotMax1 = cgi['RotMax1'] strRotDelta1 = cgi['RotDelta1'] strRotMin2 = cgi['RotMin2'] strRotMax2 = cgi['RotMax2'] strRotDelta2 = cgi['RotDelta2'] strRotMin3 = cgi['RotMin3'] strRotMax3 = cgi['RotMax3'] strRotDelta3 = cgi['RotDelta3'] ### Set Parameter to FileNames strParamFile = strMode + ".!Mode" command = "touch " + strParamFile system(command) strParamFile = strRotMin1 + "!" + strRotMax1 + "!" + strRotDelta1 + ".!Rot1" command = "touch " + strParamFile system(command) strParamFile = strRotMin2 + "!" + strRotMax2 + "!" + strRotDelta2 + ".!Rot2" command = "touch " + strParamFile system(command) strParamFile = strRotMin3 + "!" + strRotMax3 + "!" + strRotDelta3 + ".!Rot3" command = "touch " + strParamFile system(command) command = "cp " + strInputFile + " Refer.ref3d" system(command) strHTML += "</head>" strHTML += "</html>" # Output as html cgi.out(type: "text/html") do strHTML end
Ref3DtoRef2D.htmlで設定したデータをファイルに保存します。処理後は自動的にインタラクティブ操作を終了し、以降はPIONEにてルールRef3DtoRef2Dを実行します。
#!/usr/bin/env ruby require 'cgi' cgi = CGI.new ### Header strHTML = "" strHTML += "<!DOCTYPE html>" strHTML += "<html>" strHTML += "<head>" strHTML += "<title>Result</title>" ### Auto Close strHTML += '<meta http-equiv="REFRESH" content="0;URL=?pione-action=finish">' # Main Process ## Query to Parameters Result = cgi['Result'] command = "touch " + Result + "!Flag" system(command) strHTML += "</head>" strHTML += "</html>" # Output as html cgi.out(type: "text/html") do strHTML end
index2.htmlの操作によって、決定またはやり直しを選択したときの処理です。決定のときはFinish!Flag、やり直しのときはStart!FLagを作成します。処理後は自動的にインタラクティブ操作を終了します。
これらのファイルはこちらからダウンロードできます。
パッケージ作成
コードが完成したらパッケージ作成を行います。
/PIONE$ pione package build Advanced3 info: update the package info file: local:/Eos/tutorial/SampleCode/PIONE/Advanced3/pione-package.json info: Package local:/Eos/tutorial/SampleCode/PIONE/Ref3DtoRef2D(Kinoshita)+v0.1.1.ppg has been built successfully.
実行結果
では、動作させてみましょう。
PIONE Webclientのジョブページで実行し、Interactionを選択してindexページを開きます。ここでRef3DtoRef2Dを選択すると設定画面が表示されます。
今回は下記の入力ファイルを3次元像として2次元画像を作成してみます。
最小 最大 |
0 (0, 0, 0) 3398.12 (23, 55, 41) |
入力ファイル及び回転のモード(参照: オイラー角)、角度の範囲を設定して開始ボタンを押します。
すると、上記のようにインタラクティブ操作は完了して、再びPIONEにて2次元像の作成処理が動きます。(このページは閉じて構いません)
再びInteractionボタンが赤色になりますので、開くと作成された2次元像の一覧が画像にて表示されます。この内容を見ながら「決定」か「やり直し」を選択します。
同様にインタラクティブ操作が完了しますので、ページを閉じます。
「やり直し」を選択すると、再度設定画面を開くことができます。また、「決定」を選択するとこのときの結果がouputへ出力されます。このようにして結果を確認しながら適切な設定を選び直すことが出来ます。
補足(チケットを使ってルールを制御する)
設定、処理、確認のルールが同時に動いてはそれぞれの役割が満たせませんので、チケットを使用して制御を加えてみましょう。もちろん入力ファイルや出力ファイルの関係がうまく作れていれば同時に動くことはありません。今回は念のための処置として作成します。
Main.pione
Rule Main output '*.ref3d' output '*.ref2d' output '*.mon' output '*.gif' Flow rule Start >>> SubMain >>> Result rule Finish End Rule Start output 'Start0!Flag' Action touch {$O[1]} End Rule SubMain input 'Start*!Flag' output '*.*'.all Flow rule Interaction {val : '{$I[1][1]}'.str().i()} >>> Ref3DtoRef2D {val : '{$I[1][1]}'.str().i()} End Rule Finish input 'Finish*!Flag' input '*.*-{$I[1][1]}' output '{$I[2][1]}.{$I[2][2]}' Action cp {$I[2]} {$O[1]} End
MainルールとSubMainルールにて順番を指定しています。
このファイルはこちらからダウンロードできます。
応用4(単粒子解析)
次はMakefileをベースとして単粒子解析のための処理を作ってみましょう。
問題4
元のMakefileが下記であったとします。
.SUFFIXES: .roi .smooth .shrink .pad .fit .corinfo .3dinfo .3dwholeinfo .3d4sinfo .lst .3dlst .padlst .3d .ds6 .3dwholelst .3dwhole .wholeds6 .3d4s .4sds6 .3d4sinfolst #SHELL=/bin/bash REFERENCE=1J4Z-move-shrink.stack .lst.padlst: rm -f $*.padlst for i in `cat $*.lst`; do \ FILENAME=`basename $$i .roi`; \ echo $$FILENAME; \ make $$FILENAME.pad ; \ echo $$FILENAME.pad >> $*.padlst; \ done .padlst.3dlst: rm -f $*.3dlst rm -f $*.3dwholelst for i in `cat $*.padlst`; do \ FILENAME=`basename $$i .pad`; \ make $$FILENAME.fit ; \ make $$FILENAME.3dinfo ; \ cat $$FILENAME.3dinfo >> $*.3dlst ; \ make $$FILENAME.3dwholeinfo ; \ cat $$FILENAME.3dwholeinfo >> $*.3dwholelst ; \ done .padlst.3d4sinfolst: rm -f $*.3d4sinfolst for i in `cat $*.padlst`; do \ FILENAME=`basename $$i .pad`; \ make $$FILENAME.3d4sinfo ; \ cat $$FILENAME.3d4sinfo >> $*.3d4sinfolst ; \ done .roi.smooth: mrcImageSmoothing -i $*.roi -o $*.smooth -m 1 -r 4 .smooth.shrink: mrcImageShrink -i $*.smooth -o $*.shrink -S 8 .shrink.pad: mrcImagePad -i $*.shrink -o $*.pad -W 32 -H 32 .pad.fit: mrcImageAutoRotationCorrelation -i $*.pad -r $(REFERENCE) -fit $*.fit -O $*.corinfo -n 72 -m 18 -nRot1 72 -nRot2 72 -nRot3 1 > /dev/null .fit.3dinfo: awk '/Cor/ { print $$18,$$16,$$2,$$3,$$4,"0.0"}' $*.corinfo | sort -r | sed -e s/pad/fit/ > $*.3dinfolst head -n 1 $*.3dinfolst | awk ' {print $$2,$$3,$$4,$$5,$$6,$$1'} > $*.3dinfo .fit.3dwholeinfo: awk '/Cor/ { print $$18,$$16,$$2,$$3,$$4,$$9,$$11,$$12}' $*.corinfo | sort -r | sed -e s/pad/shift/ > $*.3dwholeinfolst head -n 1 $*.3dwholeinfolst | awk ' {print $$2,$$3,$$4,$$5,$$6,$$7,$$8,$$1'} > $*.3dwholeinfo X=`awk '{print -8*$$6; }' $*.3dwholeinfo`; \ Y=`awk '{print -8*$$7; }' $*.3dwholeinfo`; \ echo $$X,$$Y; mrcImageShift -i $*.roi -o $*.shift -x $$X -y $$Y -z 0 .3dlst.3d: mrc2Dto3D -I $*.3dlst -o $*.3d -InterpolationMode 2 -Double -DoubleCounter $*.3dcounter -CounterThreshold 0.5 -m 1 -WeightMode 6 .3d.ds6: mrc2map -i $*.3d -o $*.ds6 -m 3 .3dwholelst.3dwhole: mrc2Dto3D -I $*.3dwholelst -o $*.3dwhole -InterpolationMode 2 -Double -DoubleCounter $*.3dwholecounter -CounterThreshold 0.5 -m 1 -WeightMode 6 .3dwhole.wholeds6: mrc2map -i $*.3dwhole -o $*.wholeds6 -m 3 ln -sf $*.wholeds6 $*.whole.ds6 .fit.3d4sinfo: awk '/Cor/ { print $$18,$$16,$$2,$$3,$$4,$$9,$$11,$$12}' $*.corinfo | sort -r | sed -e s/pad/shift/ > $*.3d4sinfolst head -n 1 $*.3d4sinfolst | awk ' {print $$2,$$3,$$4,$$5,$$6,$$7,$$8,$$1'} > $*.3d4sinfo X=`awk '{print -4*$$6; }' $*.3d4sinfo`; \ Y=`awk '{print -4*$$7; }' $*.3d4sinfo`; \ echo $$X,$$Y; mrcImageShrink -i $*.roi -o $*.4shrink -S 4 mrcImagePad -i $*.4shrink -o $*.pad -W 64 -H 64 mrcImageShift -i $*.roi -o $*.4shift -x $$X -y $$Y -z 0 .3d4sinfolst.3d4s: mrc2Dto3D -I $*.3d4sinfolst -o $*.3d4s -InterpolationMode 2 -Double -DoubleCounter $*.3d4scounter -CounterThreshold 0.5 -m 1 -WeightMode 6 .3d4s.4sds6: mrc2map -i $*.3d4s -o $*.4sds6 -m 3 ln -sf $*.4sds6 $*.4s.ds6
上記のMakefileは複数の電子顕微鏡画像(.roi)に対して、参照像を使って角度決定をした後に3次元再構成を実行する処理となっています。.roiファイルの一覧は.lstに書かれていて、参照像の2Dスタック(REFERENCEで設定)があることが前提となっています。このmakefileは相関マップ(.corinfo)を作成するときに1/8とした画像を使用することで速度の向上をはかっています。"make (.lstのファイル名).3d"で1/8サイズ、"make (.lstのファイル名).3dwhole"で1/1サイズ、"make (.lstのファイル名).3d4s"で1/4サイズの3次元像が作成されます。
応用問題4:上記の機能を持つパッケージSingleParticle_3DReconstruction.ppgを作成してみましょう。
解答例1
解答例を一部示します。全体はこちらをご覧下さい。元の機能に加えて、サンプルの.roiファイルと参照像2Dスタック.stackを作成できるようにしています。この場合の入力ファイルは.mrcのみです。さらに、作成された3次元像を画像としてすぐに確認できるように3方向からの投影像をgifファイルで出力しています。また、機能毎にフラグを設けて作成したいデータのみを作成できるようにしています。
実装
Main.pione
Rule Main if $Flag_ROI or $Flag_Ref input '*.mrc' end if $Flag_ROI.not() input '*.lst' input '*.roi'.all end if $Flag_Ref.not() input '*.stack' end if $Flag_3D output '{$I[1][1]}.3dlst' output '{$I[1][1]}.3d' output '{$I[1][1]}.ds6' end if $Flag_3DWhole output '{$I[1][1]}.3dwholelst' output '{$I[1][1]}.3dwhole' output '{$I[1][1]}.wholeds6' output '{$I[1][1]}.whole.ds6' end if $Flag_3D4S output '{$I[1][1]}.3d4slst' output '{$I[1][1]}.3d4s' output '{$I[1][1]}.4sds6' output '{$I[1][1]}.4s.ds6' end if $Flag_ROI output '*.tiff'.all end output '*.gif'.all Flow if $Flag_ROI rule Create_SampleROI end if $Flag_Ref rule Create_stack end rule Preprocess rule Create_fit if $Flag_3D rule Create_3dlst rule Create_3d end if $Flag_3DWhole rule Create_3dwholelst rule Create_3dwhole end if $Flag_3D4S rule Create_3d4slst rule Create_3d4s end rule Projection_3d End
Main.pione内で使用しているルールは下記のようにMakefileの処理と対応しています。
Main.pione内のルール | サブルール | Makefile | 説明 |
---|---|---|---|
Create_SampleROI | - | なし | サンプル用の.roiファイルを複数作成 |
Create_stack | - | なし | 参照像の2Dスタック.stackファイルを作成 |
Preprocess | pad_padlst roi_smooth |
.lst.padlst .roi.smooth |
.padファイル一覧を作成 .roiの平滑化して.smoothを作成 |
Create_fit | - | .pad.fit | .padから.stackを使って相関マップ.corinfoを作成(最も近い画像.fitも作成) |
Create_3dlst | fit_3dinfo Sum_3dinfo_3dlst |
.fit.3dinfo | 各相関マップ.corinfoから相関値が最大の角度を集めてリスト.3dlstを作成 |
Create_3d | Reconstruct_3dlst_3d Convert_3d_ds6 |
.3dlst.3d .3d.ds6 |
リスト.3dlstと.fitを使って3次元像.3d及び.ds6を作成(1/8サイズ) |
Create_3dwholelst | fit_3dwholeinfo Sum_3dwholeinfo_3dwholelst |
.fit.3dwholeinfo | Create_3dlstの1/1サイズバージョン |
Create_3dwhole | Reconstruct_3dwholelst_3dwhole Convert_3dwhole_wholeds6 |
.3dwholelst.3dwhole .3dwhole.wholeds6 |
Create_3dの1/1サイズバージョン |
Create_3d4slst | fit_3d4sinfo Sum_3d4sinfo_3d4slst |
.fit.3d4sinfo | Create_3dlstの1/4サイズバージョン |
Create_3d4s | Reconstruct_3d4slst_3d4s Convert_3d4s_4sds6 |
.3d4sinfolst.3d4s .3d4s.4sds6 |
Create_3dの1/4サイズバージョン |
Projection_3d | - | なし | 作成された3次元像.3dなどを3方向から投影したgif画像を作成 |
また、下記のように画像サイズや回転方法をパラメータにて設定できるようにしています。
Parameter.pione
# basic param ## For Size param $X_SIZE := 80 param $Y_SIZE := 80 param $SHRINK := 8 param $SHRINK4S := 4 ## For AutoRotationCorrelation param $N_ROT := 72 param $N_ROT1 := 72 param $N_ROT2 := 72 param $N_ROT3 := 1 ## For RefData param $REF_ROT_MODE := "XOYS" param $REF_ROT1_START := 0 param $REF_ROT1_END := 359 param $REF_ROT1_DELTA := 15 param $REF_ROT2_START := 0 param $REF_ROT2_END := 359 param $REF_ROT2_DELTA := 15 param $REF_ROT3_START := 0 param $REF_ROT3_END := 0 param $REF_ROT3_DELTA := 15 ## For SampleROI param $ROI_ROT_MODE := "XOYS" param $ROI_ROT1_START := 0 param $ROI_ROT1_END := 359 param $ROI_ROT1_DELTA := 30 param $ROI_ROT2_START := 0 param $ROI_ROT2_END := 359 param $ROI_ROT2_DELTA := 30 param $ROI_ROT3_START := 0 param $ROI_ROT3_END := 0 param $ROI_ROT3_DELTA := 30 param $ROI_SN := 1 # advanced param advanced param $Flag_ROI := true advanced param $Flag_Ref := true advanced param $Flag_3D := true advanced param $Flag_3DWhole := true advanced param $Flag_3D4S := true
Makefile との動作比較
作成したPIONE定義書と元のMakefileとの処理時間を比較して、ここまでの実装の効果があったかを検証します。今回は.3dファイルの作成に関して比較します。
.roiファイルと.stackファイルは予めParameter.pioneを下記のようにして作成しています。(ROI画像: サイズ256×256, 2つの角度0 ~ 300: 60刻み)
param $X_SIZE := 256 #80 param $Y_SIZE := 256 #80 param $ROI_ROT1_START := 0 param $ROI_ROT1_END := 359 param $ROI_ROT1_DELTA := 60 #30 param $ROI_ROT2_START := 0 param $ROI_ROT2_END := 359 param $ROI_ROT2_DELTA := 60 #30 param $ROI_ROT3_START := 0 param $ROI_ROT3_END := 0 param $ROI_ROT3_DELTA := 30 advanced param $Flag_ROI := true advanced param $Flag_Ref := true advanced param $Flag_3D := false advanced param $Flag_3DWhole := false advanced param $Flag_3D4S := false
最小 最大 |
0 (0, 0, 0) 3398.12 (23, 55, 41) |
作成されたROIファイルの一覧をData.lstに書き込んでMakefileで動作できるようにします。また、Makefile内にてREFERENCEを作成した.stackファイル名に変更します。
#REFERENCE=1J4Z-move-shrink.stack REFERENCE=1WDC.stack
以上の.roiファイル、.stackファイル、Makefileを入力用のディレクトリに置きます。
Makefileの処理時間の下記のPIONE定義書を作って、計測しています。
計測①コード
Rule Main input '*.lst' input '*.roi'.all input 'Makefile' input '*.stack'.all output '{$I[1][1]}.ds6' Action make {$O[1]} End
上記のルールの場合、(全体の処理)=(Makefile処理)+(入力ファイル処理)+(出力ファイル処理)となっているので、下記の計測②時間との差分を算出すれば、およそのMakefileの処理時間が算出できます。
計測②コード
Rule Main input '*.lst' input '*.roi'.all input 'Makefile' input '*.stack'.all input '*.ds6' output '{$I[5][1]}.ds6out' Action cp {$I[5]} {$O[1]} End
計測①が9分強、計測②が1秒程度なので、Makefileの処理時間は9分強掛かったことが分かりました。
では、今回実装した処理をタスク数2(-t 2)で実行してみます。
-t 2の結果(7分程度)
今回の実装においては2分程度の削減で、処理時間がMakefileの7/9となりました。しかし、この実装においてルールを細かく分けすぎているためにルール間の入出力ファイルのやり取りによる処理が各所で発生しています。次の項目にてこれらの無駄を省くためのコーディングを考えて、さらに時間を削減してみましょう。
速度アップのための実装
リスト作成の省略
まずは.lstなどを作成している部分について考えます。PIONEは1:複数の処理が可能ですのでリストの作成は省略できます。
Main.pione
Rule Main if ($Mode_ROI + $Mode_Ref) != 0 input '*.mrc' end if $Mode_Ref == 0 input '*.stack' end if $Mode_ROI == 0 input '*.roi'.all end if $Flag_3D output '{$I[1][1]}.3dlst' output '{$I[1][1]}.3d' output '{$I[1][1]}.ds6' end if $Flag_3DWhole output '{$I[1][1]}.3dwholelst' output '{$I[1][1]}.3dwhole' output '{$I[1][1]}.wholeds6' output '{$I[1][1]}.whole.ds6' end if $Flag_3D4S output '{$I[1][1]}.3d4slst' output '{$I[1][1]}.3d4s' output '{$I[1][1]}.4sds6' output '{$I[1][1]}.4s.ds6' end if $Mode_ROI != 0 output '*.tiff'.all end if $Flag_Projection output '*.gif'.all end Flow case $Mode_ROI when 1 rule Create_Sample3d rule Create_SampleROI_each {rot1: (0.upto($roi_rot1_loop))*$ROI_ROT1_DELTA, rot2: (0.upto($roi_rot2_loop))*$ROI_ROT2_DELTA, rot3: (0.upto($roi_rot3_loop))*$ROI_ROT3_DELTA} when 2 rule Create_Sample3d rule Create_SampleROI_all end if $Mode_Ref != 0 rule Create_stack end rule Preprocess case $Mode_Cor when 1 rule Create_fit_each when 2 rule Create_fit_all end if $Flag_3D rule Create_3dlst {filename: $I[1][1].str()} rule Create_3d end if $Flag_3DWhole rule Create_3dwholelst {filename: $I[1][1].str()} rule Create_3dwhole end if $Flag_3D4S rule Create_3d4slst {filename: $I[1][1].str()} rule Create_3d4s end if $Flag_Projection rule Projection_3d end End
.roiファイル、.padファイルの一覧を記述した.lstファイル.padlstファイルを廃止して、入力用ディレクトリにある全ての.roiファイルについて処理を行うように変更しました。(但し、.3dlstなどは角度情報として使用するのでそのまま)また、入力ファイル1つに対して複数のファイルを作成する箇所についてeachで行うかallで行うかをパラメータによって決められるようにしています。今回の場合はeachを使用して複数のタスクで実行すると速度の改善が期待できます。(参照:#基本4の補足(eachでの速度検証))
eachで実行した場合(約81秒)(allよりも時間が掛かっている)
eachを-t 2で実行した場合(約47秒)(速度が改善されている)
eachを-t 3で実行した場合(約36秒)(さらに速度が改善されている)
eachを-t 4で実行した場合(約31秒)(若干速度が改善されている)
上記は1つのマシンにおいてallと複数のタスクによるeachの速度の違いです。eachを複数のタスクで動作させると、allの場合よりも速く処理できることが分かります。そして、タスク数が多いほどが速度が改善されています。しかし、タスク数を増やす毎に改善の度合いは緩やかになっています。マシン毎で一度に処理できるタスク数を把握し、全てのタスクをいくつに分けるかを考えると良いでしょう。
Param.pione
# basic param ## For Size param $X_SIZE := 80 param $Y_SIZE := 80 param $SHRINK := 8 param $SHRINK4S := 4 ## For AutoRotationCorrelation param $N_ROT := 72 param $N_ROT1 := 72 param $N_ROT2 := 72 param $N_ROT3 := 1 ## For RefData param $REF_ROT_MODE := "XOYS" param $REF_ROT1_START := 0 param $REF_ROT1_END := 359 param $REF_ROT1_DELTA := 15 param $REF_ROT2_START := 0 param $REF_ROT2_END := 359 param $REF_ROT2_DELTA := 15 param $REF_ROT3_START := 0 param $REF_ROT3_END := 0 param $REF_ROT3_DELTA := 15 ## For SampleROI param $ROI_ROT_MODE := "XOYS" param $ROI_ROT1_START := 0 param $ROI_ROT1_END := 359 param $ROI_ROT1_DELTA := 30 $roi_rot1_loop := ($ROI_ROT1_END - $ROI_ROT1_START) / $ROI_ROT1_DELTA param $ROI_ROT2_START := 0 param $ROI_ROT2_END := 359 param $ROI_ROT2_DELTA := 30 $roi_rot2_loop := ($ROI_ROT2_END - $ROI_ROT2_START) / $ROI_ROT2_DELTA param $ROI_ROT3_START := 0 param $ROI_ROT3_END := 0 param $ROI_ROT3_DELTA := 30 $roi_rot3_loop := ($ROI_ROT3_END - $ROI_ROT3_START) / $ROI_ROT3_DELTA param $ROI_SN := 1 # advanced param ## Mode 0: do not, 1: each, 2: all advanced param $Mode_ROI := 1 advanced param $Mode_Ref := 1 advanced param $Mode_Cor := 1 ## Flag true: do, false: do not advanced param $Flag_3D := true advanced param $Flag_3DWhole := true advanced param $Flag_3D4S := true advanced param $Flag_Projection := true
今回はMode_ROIとMode_Corのみ1: each, 2: allの切り替えが可能としています。$roi_rot1_loopなどの変数束縛を追加していますが、これはeachのループ数のためでユーザが入力する必要はありません。
これらのファイルはこちらからダウンロードできます。
同じようなルールをまとめる
Preprocess
Mainで呼んでいるPreprocessのルールは下記のようにしていました。
Main.pione内のルール | サブルール | Makefile | 説明 |
---|---|---|---|
Preprocess | pad_padlst roi_smooth |
.lst.padlst .roi.smooth |
.padファイル一覧を作成 .roiの平滑化して.smoothを作成 |
これらの処理は全て入力ファイルと出力ファイルが1:1対応であり、.roi -> .smooth -> .shrinkの順番に作成して最終的に.padファイルのみを得ることを目的としています。このような場合はルールを分けずに1つのルールの中で処理を行った方がルール間のファイルのやり取りが最小限で済みます。(参照:#基本5の補足)
コード | 処理結果(ProM) | |
---|---|---|
(1) 分けた場合 |
Rule Preprocess input '*.roi' output '{$I[1][1]}.pad' Flow rule roi_smooth; rule smooth_shrink; rule shrink_pad; End #.roi.smooth: Rule roi_smooth input '*.roi' output '{$I[1][1]}.smooth' Action mrcImageSmoothing -i {$I[1]} -o {$O[1]} -m 1 -r 4 End #.smooth.shrink: Rule smooth_shrink input '*.smooth' output '{$I[1][1]}.shrink' Action mrcImageShrink -i {$I[1]} -o {$O[1]} -S {$SHRINK} End #.shrink.pad: Rule shrink_pad input '*.shrink' output '{$I[1][1]}.pad' Action width=`expr {$X_SIZE} / {$SHRINK}` height=`expr {$Y_SIZE} / {$SHRINK}` mrcImagePad -i {$I[1]} -o {$O[1]} -W ${width} -H ${height} End |
|
(2) まとめた場合 |
Rule Preprocess input '*.roi' output '{$I[1][1]}.pad' Action # roi_smooth mrcImageSmoothing -i {$I[1]} -o {$I[1][1]}.smooth -m 1 -r 4 # smooth_shrink mrcImageShrink -i {$I[1][1]}.smooth -o {$I[1][1]}.shrink -S {$SHRINK} # shrink_pad width=`expr {$X_SIZE} / {$SHRINK}` height=`expr {$Y_SIZE} / {$SHRINK}` mrcImagePad -i {$I[1][1]}.shrink -o {$O[1]} -W ${width} -H ${height} End |
上記の(2)のようにルールをまとめることで(1)に比べて、1ファイル毎に1秒ほど時間が短縮できました。
Create_fit, Create_3dlst, Create_3d
Create_fitは各.padファイルから.fitファイルや.corinfoファイルをそれぞれ1:1で作成しています。また、Create_3dlstでもfit_3dinfoまでは1:1対応の処理です。
Main.pione内のルール | サブルール | Makefile | 説明 |
---|---|---|---|
Create_fit | - | .pad.fit | .padから.stackを使って相関マップ.corinfoを作成(最も近い画像.fitも作成) |
Create_3dlst | fit_3dinfo Sum_3dinfo_3dlst |
.fit.3dinfo | 各相関マップ.corinfoから相関値が最大の角度を集めてリスト.3dlstを作成 |
Create_3d | Reconstruct_3dlst_3d Convert_3d_ds6 |
.3dlst.3d .3d.ds6 |
リスト.3dlstと.fitを使って3次元像.3d及び.ds6を作成(1/8サイズ) |
従って、この部分もまとめた方が良いでしょう。同様にfit_3dwholeinfoとfit_3d4sinfoもまとめます。この処理は前項目のPreprocessともまとめられそうですが、単粒子解析のための3次元再構成では角度決定処理を繰り返し行うので、この部分は分けて実装します。(参照:単粒子解析#繰り返し(精密化))また、Create_3dlstのSum_3dinfo_3dlstとCreate_3dなども一つにまとめられます。
まとめたCreate_fit_each
Rule Create_fit_each input '*.pad' input '*.stack' if ($Flag_3DWhole or $Flag_3D4S) input '{$I[1][1]}.roi' end if $Flag_3D output '{$I[1][1]}.fit' output '{$I[1][1]}.3dinfo' end if $Flag_3DWhole output '{$I[1][1]}.shift' output '{$I[1][1]}.3dwholeinfo' end if $Flag_3D4S output '{$I[1][1]}.4shift' output '{$I[1][1]}.3d4sinfo' end Action # Create_fit mrcImageAutoRotationCorrelation -i {$I[1]} -r {$I[2]} -fit {$I[1][1]}.fit -O {$I[1][1]}.corinfo \ -n {$N_ROT} -m 18 -nRot1 {$N_ROT1} -nRot2 {$N_ROT2} -nRot3 {$N_ROT3} > /dev/null # fit_3dinfo if {$Flag_3D} ; then awk '/Cor/ { print $18,$16,$2,$3,$4,"0.0"}' {$I[1][1]}.corinfo | sort -r | sed -e s/pad/fit/ > {$I[1][1]}.3dinfolst head -n 1 {$I[1][1]}.3dinfolst | awk ' {print $2,$3,$4,$5,$6,$1'} > {$I[1][1]}.3dinfo fi # fit_3dwholeinfo if {$Flag_3DWhole} ; then awk '/Cor/ { print $18,$16,$2,$3,$4,$9,$11,$12}' {$I[1][1]}.corinfo | sort -r | sed -e s/pad/shift/ > {$I[1][1]}.3dwholeinfolst head -n 1 {$I[1][1]}.3dwholeinfolst | awk ' {print $2,$3,$4,$5,$6,$7,$8,$1'} > {$I[1][1]}.3dwholeinfo X=`awk '{print -{$SHRINK}*$6; }' {$I[1][1]}.3dwholeinfo`; Y=`awk '{print -{$SHRINK}*$7; }' {$I[1][1]}.3dwholeinfo`; echo $X,$Y; mrcImageShift -i {$I[1][1]}.roi -o {$I[1][1]}.shift -x $X -y $Y -z 0 fi # fit_3d4sinfo if {$Flag_3D4S} ; then awk '/Cor/ { print $18,$16,$2,$3,$4,$9,$11,$12}' {$I[1][1]}.corinfo | sort -r | sed -e s/pad/4shift/ > {$I[1][1]}.3d4sinfolst head -n 1 {$I[1][1]}.3d4sinfolst | awk ' {print $2,$3,$4,$5,$6,$7,$8,$1'} > {$I[1][1]}.3d4sinfo X=`awk '{print -{$SHRINK4S}*$6; }' {$I[1][1]}.3d4sinfo`; Y=`awk '{print -{$SHRINK4S}*$7; }' {$I[1][1]}.3d4sinfo`; echo $X,$Y; mrcImageShrink -i {$I[1][1]}.roi -o {$I[1][1]}.4shrink -S {$SHRINK4S} width=`expr {$X_SIZE} / {$SHRINK4S}` height=`expr {$Y_SIZE} / {$SHRINK4S}` mrcImagePad -i {$I[1][1]}.4shrink -o {$I[1][1]}.4spad -W ${width} -H ${height} mrcImageShift -i {$I[1][1]}.4spad -o {$I[1][1]}.4shift -x $X -y $Y -z 0 fi End
まとめたCreate_3d
Rule Create_3d input '*.3dinfo'.all input '*.fit'.all output '{$filename}.3d' output '{$filename}.ds6' param $filename Action # Sum_3dinfo_3dlst for info in {$I[1]} do cat $info >> {$filename}.3dlst done # Reconstruct_3dlst_3d mrc2Dto3D -I {$filename}.3dlst -o {$O[1]} -InterpolationMode 2 -Double -DoubleCounter {$filename}.3dcounter -CounterThreshold 0.5 -m 1 -WeightMode 6 # Convert_3d_ds6 mrc2map -i {$O[1]} -o {$O[2]} -m 3 End
Create_3d4s, Create_3dwholeについても同様にまとめています。
動作比較
作成したPIONE定義書と前回分や元のMakefileとの処理時間を比較して、ここまでの実装の効果があったかを検証します。今回は.3dファイルの作成に関して比較します。
では、今回の実装による処理時間を複数のタスク(-t 2と-t 4)で実行してみます。
1つのマシンにおいて2つのタスクに分けることで4分削減して約5/9(改善前からは2分削減して約5/7)になりました。さらに4つのタスクに分けることで6分削減して約1/3になりました。入力ファイル(256×256:36枚)の場合において、今回の実装で有効な並列処理ができたと言えるでしょう。
機能追加のための実装
今回は作成したPIONE定義書やパッケージについてさらに機能を追加する方法を考えます。
粒子の切り出しを追加する
.roiファイルが無い場合にユーザが切り出した画像を使用できるように追加してみましょう。(参照:単粒子解析#電子顕微鏡画像から粒子画像の抽出)
この機能は.roiファイルを作成するものなので、Create_ROIと同列と考えて、$Mode_ROIに3を追加してこのときにユーザが切り出すようにします。
Main.pione(Cutout_ROIを追加)
case $Mode_ROI when 1 rule Create_Sample3d rule Create_SampleROI_each {rot1: (0.upto($roi_rot1_loop))*$ROI_ROT1_DELTA, rot2: (0.upto($roi_rot2_loop))*$ROI_ROT2_DELTA, rot3: (0.upto($roi_rot3_loop))*$ROI_ROT3_DELTA} when 2 rule Create_Sample3d rule Create_SampleROI_all when 3 rule Cutout_ROI end
Parameter.pione(コメントを追加)
## Mode 0: do not, 1: each, 2: all, 3: use Display2 advanced param $Mode_ROI := 3
Create_SampleROI.pione(Cutout_ROIを追加)
Rule Cutout_ROI input '*.mrc' output '*.roi'.all output '*.tiff'.all Action Display2 -i {$I[1]} for data in $(ls *.roi) do mrc2tiff -i ${data} -o ${data}.tiff done End
切り出しにはDisplay2を使用しています。
新しいルールを作成して、呼び出すだけで簡単に機能を追加することができました。
繰り返し機能を追加する
単粒子解析のための3次元再構成は出来上がった3次元像を参照像として繰り返して、3次元像の精密化を行うこともあります。今回はこの部分の対応を作成してみましょう。
Parameter.pione(繰り返し回数を追加)
## For repeat count param $Repeat_Count := 2
Repeat_3d
Rule Repeat_3d input '*-{$count}.3d' output '{$I[1][1]}-{$count + 1}.stack' param $count Action mrc3Dto2D -i {$I[1]} -o {$O[1]} -m 1 -InterpolationMode 2 -EulerMode {$REF_ROT_MODE} \ -Rot1 {$REF_ROT1_START} {$REF_ROT1_END} {$REF_ROT1_DELTA} \ -Rot2 {$REF_ROT2_START} {$REF_ROT2_END} {$REF_ROT2_DELTA} \ -Rot3 {$REF_ROT3_START} {$REF_ROT3_END} {$REF_ROT3_DELTA} End
上記のように作成された.3dファイルから.stackファイルを作成します。このとき繰り返し回数が分かるようにファイル名にを付加しています。また、下記のルールについても繰り返し回数の対応を行います。
Create_stack.pione
output '{$I[1][1]}-1.stack'
Main.pione(出力ファイルの宣言)
if $Flag_3D output '{$I[1][1]}-{$Repeat_Count}.3d' output '{$I[1][1]}-{$Repeat_Count}.ds6' end
繰り返しの最後のファイルのみを出力するようにします。
Main.pione(Flow)
case $Mode_3D when 1 rule Create_fit_each if $Flag_3D rule Create_3d {filename: $I[1][1].str(), count: 1.upto($Repeat_Count)} rule Repeat_3d {count: 1.upto($Repeat_Count - 1)} end
Create_3d, Repeat_3dを繰り返し回数分呼び出します。
Create_stack.pione(最初の.stackファイル名に1を付加)
output '{$I[1][1]}-1.stack'
Create_fit_each.pione(.3dのための処理)
Rule Create_fit_each input '*.pad' input '*-*.stack' if $Flag_3D output '{$I[1][1]}.fit-{$I[2][2]}' output '{$I[1][1]}.3dinfo-{$I[2][2]}' end Action # Create_fit mrcImageAutoRotationCorrelation -i {$I[1]} -r {$I[2]} -fit {$I[1][1]}.fit-{$I[2][2]} -O {$I[1][1]}.corinfo-{$I[2][2]} \ -n {$N_ROT} -m 18 -nRot1 {$N_ROT1} -nRot2 {$N_ROT2} -nRot3 {$N_ROT3} > /dev/null # fit_3dinfo if {$Flag_3D} ; then awk '/Cor/ { print $18,$16,$2,$3,$4,"0.0"}' {$I[1][1]}.corinfo-{$I[2][2]} | sort -r | sed -e s/pad/fit-{$I[2][2]}/ > {$I[1][1]}.3dinfolst-{$I[2][2]} head -n 1 {$I[1][1]}.3dinfolst-{$I[2][2]} | awk ' {print $2,$3,$4,$5,$6,$1'} > {$I[1][1]}.3dinfo-{$I[2][2]} fi
Create_3d.pione
Rule Create_3d input '*.3dinfo-{$count}'.all input '*.fit-{$count}'.all output '{$filename}-{$count}.3d' output '{$filename}-{$count}.ds6' param $filename param $count Action # Sum_3dinfo_3dlst for info in {$I[1]} do cat $info >> {$filename}-{$count}.3dlst done # Reconstruct_3dlst_3d mrc2Dto3D -I {$filename}-{$count}.3dlst -o {$O[1]} -InterpolationMode 2 -Double -DoubleCounter {$filename}-{$count}.3dcounter -CounterThreshold 0.5 -m 1 -WeightMode 6 # Convert_3d_ds6 mrc2map -i {$O[1]} -o {$O[2]} -m 3 End
このように実装することで指定した数Repeat_Countに応じた繰り返し処理を行うように機能追加ができます。
応用5(クラスター解析)
次はクラスター解析のMakefileを元にして速度改善を考えてみましょう。こちらのMakefileを元にしています。今回はMakefileのコマンドも使用します。これにより全体の実装量を削減できますが、makeコマンド実行するときに入出力ファイルがそれぞれ何に当たるのかを注意する必要があります。
前処理
前処理の並列化を考えます。これまでのPIONE定義書ではeachを使って1ファイル毎の並列処理として実装を行いました。今回はいくつかのグループに分けて、そのグループ毎で並列化を行うようにしてみます。
param $task := 2 Rule Main input '*.roi'.all input 'Makefile' input 'Makefile.config' output '*.pad'.all Flow rule Preprocess {filelist:$I[1].all, num:1.upto($task), length:$I[1].length()} End Rule Preprocess input $filelist.nth(((($num-1)*((($length-1)/$task)+1))+1).upto((($num*((($length-1)/$task)+1))|$length).min())) input 'Makefile' input 'Makefile.config' output '*.pad'.all Action for data in {$I[1]} do make $(basename ${data} ".roi").pad done End
Preprocess内の処理はMakefile内のコマンドに委ねるようにしています。入力ファイルがかなり複雑となっていますが、これは次のように考えています。
まずパラメータtaskで分割したいグループ数を決め、この数だけルールPreprocessの呼び出すように考えます。
- filelistにはファイル自体でなく、ファイル名の一覧を送る
- numには分割数の内の何番目かを送る
- lengthにはファイル名の要素数を送る
次にPreprocess側では受け取ったパラメータからグループ毎で使用するファイル名を決め、それらを入力ファイルとすることで分割を実現します。なお、今回使用しているupto, length, minなどのメソッドについてはPIONEの式を参照して下さい。
次のグラフは上記の前処理を256, 512, 1024, 2048, 4096(pixel)の画像ファイル100枚を入力としてタスク数1, 2, 4, 10で動作したときの処理時間です。
allは(前処理に関する)全体の処理、processは前処理自体、otherはそれ以外のファイル通信などの処理時間を表しています。
ここでタスク数を増やしたときの変化に注目します。全体の処理時間についてt=1(シングルタスク)との比率は下記のようになりました。
2048サイズまでは処理時間が短くなっています。今回はタスク数を増やしても速度の改善は見られません。タスク数を増やすことによって前処理自体の時間は軽減されますが、それ以外のファイル通信など処理で逆に時間がかかってしまうためです。
前処理はタスク数が多い方が速くなる(このマシンではt=4まで) | タスク数が多くなると並列処理の準備(ファイル通信など)は時間がかかる。 |