Univa Grid EngineからPBS Torqueに移行するときに工夫が必要なこと

まずはこちらを参照してください→ [SGE用スクリプトファイルをPBS用に書き換える方法](http://www.nibb.ac.jp/cproom/wiki3/index.php/SGE%E7%94%A8%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92PBS%E7%94%A8%E3%81%AB%E6%9B%B8%E3%81%8D%E6%8F%9B%E3%81%88%E3%82%8B%E6%96%B9%E6%B3%95)。本記事ではこちらで扱われていない内容を記します。

## アレイジョブをqdelする
PBSではアレイジョブは`1234[]`といったjob IDがつきます。これを削除するには、bashなどがパス名展開して余計なことをしないよう`qdel "1234[]"`とダブル（orシングル）クォーテーションで囲む必要があります。`qhold`などのコマンドを実行する際にも同様です。

## qsubを実行したディレクトリをカレントディレクトリにする（が、直接実行もいけるようにする）
UGEでは`$# -cwd`で済んでいましたが、PBSではこれが使えないので、スクリプトの頭のほうで`cd ${PBS_O_WORKDIR}`します。しかしそれだとPBSを使わずに直接実行した際には`cd (空文字列)`が評価されてしまいます。単純に以下で回避しましょう。

```bash
cd "${PBS_O_WORKDIR:-$(pwd)}"
```

蛇足ですが、PBSのアレイジョブで実行するスクリプトを直接実行もできるようにする際にも同様のテクニックが便利です。すなわち、`input/file1` ... `input/file100`まで処理したいファイルがあるとき、以下のようにするとアレイジョブではすべてのファイルを処理し、直接実行すると1番目のファイルだけを単一プロセスで処理するようにできます。

```bash
find input/ -type f | sort | awk "NR==${PBS_ARRAY_INDEX:-1}"
```

## JOBIDやARRAYIDを付加してログを出力する
UGEでは以下のようqsubへの引数を書くことで、ログファイル名を一意に定めることができました。また、少々ややこしいことにスクリプト内では`$SGE_TASK_ID`でアレイジョブの何番目かを取ってくる仕様でした。

```bash
#$ -o log/stdout_$HOSTNAME_$JOB_ID_$TASK_ID
#$ -e log/stderr_$HOSTNAME_$JOB_ID_$TASK_ID
```

PBSを使用する際、スクリプト内では`$SGE_TASK_ID`の代わりに`$PBS_ARRAY_INDEX`を使うことができます。しかし`$JOB_ID`と`$TASK_ID`に相当するものがありません。

そこで代替策としては、実行するスクリプトの内容をすべて`{}`でグループコマンド化し、まるごと標準出力と標準エラー出力をリダイレクトしてやる手があります（もっといい方法があったら教えてください！）。

```bash
#!/bin/bash
#PBS -S /bin/bash
#PBS -N demojob
#PBS -V
#PBS -o /dev/null
#PBS -e /dev/null
#PBS -J 1-4

cd "${PBS_O_WORKDIR:-$(pwd)}"
{
    echo hoge
    echo fuga
    python nonexistent.py
}&>log/$(date +"%Y%m%d%H%M%S")_"${PBS_JOBNAME}_${PBS_JOBID:--$$}"
```

このようにすると以下のログ出力が得られます。`cd`はグループに入る前に実行する必要があることに注意してください。ファイル名にはついでに日時も付加してあります。なお、PBSを経由しないで実行するときのために`$PBS_JOBID`がなければプロセスIDが入るようにしてあります。

```log/20200101123456_demojob_1234[4].hostname
hoge
fuga
python: can't open file 'nonexistent.py': [Errno 2] No such file or directory
```

## スクリプトに引数を与える際の留意点
UGEでは`qsub myscript.sh myarg`とするだけでした。PBSでは`qsub -- $HOME/mydir/myscript.sh myarg`とすれば与えることができます。このときは絶対パスでスクリプトを指定する必要があることに注意してください。しかし、これをするとスクリプト本体内で`#PBS ...`と指定しているものが無効になってしまいます。たとえば`#PBS -V`も無効になりますから、PATHの設定が消えてしまいますし、`#PBS -J ...`も消えてアレイジョブになりません（このトレードオフを解消する方法をご存じの方はぜひ教えてください！）。

いまのところの対応策は
1. スクリプト内にqsubへの引数を書くのをやめて、`qsub -V -J 1-4 -- $HOME/mydir/myscript.sh myarg`など直接qsubの引数にする
2. スクリプトに引数を渡すことをあきらめて、qsub実行前に`export MYVAR=foobar`など環境変数を設定したうえで単に`qsub myscript.sh`して、スクリプト内で環境変数を読み取る。コマンドがすっきりするので個人的にはこちらが好みです。

## ジョブ一つあたりのCPU数を指定する
上記記事で扱われている内容なので冗長ですが、よく使うので書いておきます。一つのジョブが10CPUを使うときにUGEでは`#$ -pe make 10`など指定したところ、PBSでは`#PBS -l select=10`としてください。`#PBS -l select=10:mem=10GB`なら10CPUとメモリ10GBを確保します。

## スクリプトひな形
ここまでの内容を合わせて、スクリプトのひな型はこんな感じになります。環境変数による切り替えの例として`DRYRUN`が空でなければ実際の処理をスキップするようにしてあります。Queueの設定などはそれぞれの環境に合わせて行ってください。

```bash
#!/bin/bash
#PBS -S /bin/bash
#PBS -N demojob
#PBS -o /dev/null
#PBS -e /dev/null
#PBS -V
#PBS -J 1-4

cd "${PBS_O_WORKDIR:-$(pwd)}"
{
    INPUTFILE=$(find input/ -type f | sort | awk "NR==${PBS_ARRAY_INDEX:-1}")
    echo "Executing something for ${INPUTFILE}"

    if [ -n "${DRYRUN}" ]; then
        echo "----Dry run flag is set... skipping execution"
    else
        some_command_you_want_to_run "${INPUTFILE}"
    fi

    echo "----Done"
}&>log/$(date +"%Y%m%d%H%M%S")_"${PBS_JOBNAME}_${PBS_JOBID:--$$}"
```