FrontISTRをAWS ParallelCluster バージョン3で利用する③

# 0. はじめに

前回の記事では，ParallelClusterの立ち上げとログインまでを扱いました．そちらをご覧になっていない方は，下記「シリーズの流れ」にリンクを記載していますので，参考にしてください．今回は，ParallelCluster上でのFrontISTR実行について，ジョブスケジューラであるSlurmの使い方・ジョブスクリプトの書き方を中心に見ていきます．

# 1. シリーズの流れ

1. [ParallelCluster バージョン3用のカスタムAMIの作成](https://qiita.com/chikasuiro/items/b4b7899437e6e8e126ff)
1. [ParallelCluster バージョン3用の設定ファイルの作成〜起動](https://qiita.com/chikasuiro/items/f95fdffc4e56e58fc27e)
1. Slurmを利用したFrontISTR並列実行 (この記事)

---

# 2. ログイン〜領域分割

[前回の記事](https://qiita.com/chikasuiro/items/f95fdffc4e56e58fc27e)に沿って，クラスタの作成とログインを行います．

設定ファイル「v3.1.2-FrontISTR.yaml」を利用して，「FrontISTR-cluster」という名称のクラスタを作成するコマンドは次の通りです．

```shell
pcluster create-cluster -n FrontISTR-cluster -c v3.1.2-FrontISTR.yaml
```

作成が完了したのを確認したら，ヘッドノードにログインします(ここでは`pcluster ssh`コマンドを利用します)．

```shell
pcluster ssh -n FrontISTR-cluster -i PRIVATE_KEY
```

子ノード(計算資源)との共有ディレクトリとして「/shared」を指定したので，そちらに移動し，例題となるモデル(サポートリポジトリ)をGitLabから持ってきます(書籍p. 235と同じ内容です)．

```shell
cd /shared
git clone https://gitlab.com/FrontISTR-Commons/nonlinearparallelfem.git
cd nonlinearparallelfem/SampleModels/OpenTool
```

並列計算をするに当たり，モデルの領域分割を行います．今回は比較的小さいものですので，ジョブを投げる(=子ノードを立ち上げる)ことなく，その場で実行します．

```shell
hecmw_part1
```

ここでは，領域分割したメッシュを別のディレクトリに格納するよう設定しています[^2]ので，「MESH」というディレクトリが作成されます．

```shell
$ ls
frontistr.job   hecmw_part_ctrl.dat  MESH      mesh.inp
hecmw_ctrl.dat  hecmw_part.log       mesh.cnt  ucd.inp
$ ls MESH
mesh.dist.0   mesh.dist.2   mesh.dist.30  mesh.dist.41  mesh.dist.52  mesh.dist.63
mesh.dist.1   mesh.dist.20  mesh.dist.31  mesh.dist.42  mesh.dist.53  mesh.dist.64
mesh.dist.10  mesh.dist.21  mesh.dist.32  mesh.dist.43  mesh.dist.54  mesh.dist.65
mesh.dist.11  mesh.dist.22  mesh.dist.33  mesh.dist.44  mesh.dist.55  mesh.dist.66
mesh.dist.12  mesh.dist.23  mesh.dist.34  mesh.dist.45  mesh.dist.56  mesh.dist.67
mesh.dist.13  mesh.dist.24  mesh.dist.35  mesh.dist.46  mesh.dist.57  mesh.dist.68
mesh.dist.14  mesh.dist.25  mesh.dist.36  mesh.dist.47  mesh.dist.58  mesh.dist.69
mesh.dist.15  mesh.dist.26  mesh.dist.37  mesh.dist.48  mesh.dist.59  mesh.dist.7
mesh.dist.16  mesh.dist.27  mesh.dist.38  mesh.dist.49  mesh.dist.6   mesh.dist.70
mesh.dist.17  mesh.dist.28  mesh.dist.39  mesh.dist.5   mesh.dist.60  mesh.dist.71
mesh.dist.18  mesh.dist.29  mesh.dist.4   mesh.dist.50  mesh.dist.61  mesh.dist.8
mesh.dist.19  mesh.dist.3   mesh.dist.40  mesh.dist.51  mesh.dist.62  mesh.dist.9
```

---

# 3. Slurmコマンド

領域分割が完了したら，いよいよソルバの実行になります．ここではまず，ジョブスケジューラであるSlurmの主要コマンドについて，実行例と共に見ていきます．[Slurm公式ドキュメント](https://slurm.schedmd.com/quickstart.html)の他，以下のサイトが参考になります．

- [FOCUS](https://www.j-focus.jp/user_guide/ug0004020000/)
- [モナシュ大](https://docs.monarch.erc.monash.edu/MonARCH/slurm/advanced-slurm.html)
- [スタンフォード大](https://srcc.stanford.edu/sge-slurm-conversion)
- [ペンシルベニア大](https://computing.sas.upenn.edu/gpc/job/slurm)

## 3.1. ジョブの投入

ジョブの投入は`sbatch`コマンドです．ジョブスクリプトを指定します．ジョブIDが返ってきます．

```shell
$ sbatch v3.1.2-frontistr.job 
Submitted batch job 3
```

## 3.2. ジョブの削除

ジョブの削除は`scancel`コマンドです．ジョブIDを指定します．リターンはありません．

```shell
$ scancel 3
```

## 3.3. ジョブ情報の表示

ジョブの情報は`squeue`コマンドで表示します．SGEと異なり，経過時間[^3.3]も表示されます．

```shell
$ squeue 
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
                 3     queue OpenTool ec2-user CF       0:03      2 queue-dy-compute-resource-[1-2]
$ squeue 
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
                 3     queue OpenTool ec2-user  R       0:06      2 queue-dy-compute-resource-[1-2]
```

`ST`欄に表示される状態については，上に載せた[FOCUS](https://www.j-focus.jp/user_guide/ug0004020000/)のページに詳しく書かれています．ノード確保中の`CF`，実行中の`R`の他には，確保に失敗した際の`PD`や削除後の`CG`を比較的目にすると思われます．一番右の`NODELIST`については次の3.4.を参照してください．

## 3.4. ノード情報の表示

利用しているノードの情報は`sinfo`コマンドで表示します．オプション`-N`を付けると，各ノードが個別の行で，`-s`を付けると，全ノードをまとめた形で状態ごとのノード数が表示されます．

```shell
$ sinfo 
PARTITION AVAIL  TIMELIMIT  NODES  STATE NODELIST
queue*       up   infinite      2 alloc~ queue-dy-compute-resource-[1-2]
queue*       up   infinite      8  idle~ queue-dy-compute-resource-[3-10]

$ sinfo -N
NODELIST                      NODES PARTITION STATE 
queue-dy-compute-resource-1       1    queue* alloc~
queue-dy-compute-resource-2       1    queue* alloc~
queue-dy-compute-resource-3       1    queue* idle~ 
queue-dy-compute-resource-4       1    queue* idle~ 
queue-dy-compute-resource-5       1    queue* idle~ 
queue-dy-compute-resource-6       1    queue* idle~ 
queue-dy-compute-resource-7       1    queue* idle~ 
queue-dy-compute-resource-8       1    queue* idle~ 
queue-dy-compute-resource-9       1    queue* idle~ 
queue-dy-compute-resource-10      1    queue* idle~ 

$ sinfo -s
PARTITION AVAIL  TIMELIMIT   NODES(A/I/O/T) NODELIST
queue*       up   infinite         2/8/0/10 queue-dy-compute-resource-[1-10]
```

上の例の場合は，1・2番の2ノードを利用しているということになります．全体が10ノードなのは，ParallelCluster バージョン3での最大ノード数のデフォルト値が10であるためです．なお．ここで表示される`alloc`はあくまでも「10の内使用する2を設定しました」のような意味合いであり，実機を確保できているとは限りません．実機の確保に失敗した場合は，`alloc`が再び`idle`となり，別の番号のノードで確保を試みます．

---

# 4. Slurm用ジョブスクリプトの書き方

サポートリポジトリでは，SGE用のジョブスクリプト [frontistr.job](https://gitlab.com/FrontISTR-Commons/nonlinearparallelfem/-/blob/master/SampleModels/OpenTool/frontistr.job) が提供されています．こちらをSlurm用に書き換えていきます．

SGEとSlurmとの対応については，先にも挙げた[モナシュ大](https://docs.monarch.erc.monash.edu/MonARCH/slurm/advanced-slurm.html)，[スタンフォード大](https://srcc.stanford.edu/sge-slurm-conversion)，[ペンシルベニア大](https://computing.sas.upenn.edu/gpc/job/slurm)のページが参考になります．特にモナシュ大のページでは，様々な並列計算の場合について紹介されています．また，[FOCUS](https://www.j-focus.jp/user_guide/ug0004040000/)のページ(他節)も，比較はありませんが良い日本語資料となっています(以下，断りなくこれらを参照します)．

## 4.1. SGE用のものを書き換える

こちらが，サポートリポジトリに掲載されている元のジョブスクリプトです．上から順に書き換えていきましょう．

```shell:frontistr.job
#!/bin/sh
#$ -cwd
#$ -N OpenTool
#$ -pe mpi 72
#$ -j y

source ~/.bashrc

export OMP_NUM_THREADS=1

mpirun -np 72 fistr1 > out.log
```

- L. 2 `#$ -cwd`
  - 現在のワーキングディレクトリへの移動を示します．Slurmではデフォルトの挙動ですので，対応するコマンドは存在しません．
- L. 3 `#$ -N OpenTool`
  - ジョブの名称設定です．Slurmでは`#SBATCH -J JOB_NAME`と書きます．
- L. 4 `#$ -pe mpi 72`
  - MPI並列数の設定です．Slurmでは`#SBATCH --ntasks <num_procs>`もしくは`#SBATCH -n <num_procs>`と書きます．
- L. 5 `#$ -j y`
  - 標準出力と標準エラー出力を1つのファイルにまとめる設定です．Slurmでは標準出力のみを指定することで同様の結果を得ます．
- L. 11 `mpirun -np 72 fistr1 > out.log`
  - FrontISTRの並列実行を示します．並列数は決め打ちでも良いですが，Slurmでは環境変数`$SLURM_NTASKS`を利用して取得することができます．

これらを踏まえて書き直すと，以下のジョブスクリプトが完成します．名称は元のファイルにならい「v3.1.2-frontistr.job」としています．

```shell:v3.1.2-frontistr.job
#!/bin/sh
#SBATCH -J OpenTool
#SBATCH -n 72

source ~/.bashrc

export OMP_NUM_THREADS=1

mpirun -np $SLURM_NTASKS fistr1 > out.log
```

前項で扱ったコマンドを用い，実行させます．

```shell
sbatch v3.1.2-frontistr.job
```

すると，計算開始後すぐに終了してしまいました．明らかに失敗しているのでout.logを確認すると，下に示す内容が36行(1ノード分)続いていました．

```:out.log
MPI startup(): PMI server not found. Please set I_MPI_PMI_LIBRARY variable if it is not a singleton case.
MPI startup(): PMI server not found. Please set I_MPI_PMI_LIBRARY variable if it is not a singleton case.
...
```

どうやら"PMI"というものでエラーになってしまったようです．

## 4.2. 並列実行部分を修正する

どうもSlurmでの`mpirun`コマンドの利用に問題がありそうなので，Intel MPIの利用について，[Slurmのドキュメント](https://slurm.schedmd.com/mpi_guide.html#intel_mpi)を参照します．すると，`mpirun`コマンド自体は利用できるようですが，別にプロセスマネージャの利用・指定が必要なことが分かります．また，推奨されているのは`srun`コマンドを利用した方法ですので，こちらに切り替えます．

`srun`コマンドを利用するにはまず

```shell
export I_MPI_PMI_LIBRARY=/path/to/slurm/pmi/library/libpmi.so
```

としてPMI (Process Management Interface)のライブラリを指定する必要があります．このlibpmi.soという共有オブジェクトですが，「slurm libpmi.so」と検索すると，場所に関するGitHubのIssue[^4.2]が出てきました．これを基に/opt/slurm/lib内を探したところ，確かに存在しましたので，値として設定します．

`srun`コマンドの例は

```shell
srun -n <num_procs> a.out
```

と示されています．

これらを踏まえてv3.1.2-frontsitr.jobを書き直します．下3行が更新箇所です．

```shell:v3.1.2-frontistr.job
#!/bin/sh
#SBATCH -J OpenTool
#SBATCH -n 72

source ~/.bashrc

export OMP_NUM_THREADS=1
export I_MPI_PMI_LIBRARY=/opt/slurm/lib/libpmi.so

srun -n $SLURM_NTASKS fistr1 > out.log
```

再びジョブスクリプトを投入します．

```shell
sbatch v3.1.2-frontistr.job
```

投入後に`squeue`，`sinfo`コマンドを利用すると，前項のような出力を得られます．その他にも，ノードの情報を表示するコマンドとして`scontrol`，ジョブの情報を表示するコマンドとして`sstat`などがあります．

```shell
$ scontrol show nodes
NodeName=queue-dy-compute-resource-1 Arch=x86_64 CoresPerSocket=1 
   CPUAlloc=36 CPUTot=36 CPULoad=1.04
   AvailableFeatures=dynamic,c5n.18xlarge,compute-resource,efa
   ActiveFeatures=dynamic,c5n.18xlarge,compute-resource,efa
   Gres=(null)
   NodeAddr=10.0.1.76 NodeHostName=queue-dy-compute-resource-1 Version=21.08.6
   OS=Linux 4.14.268-205.500.amzn2.x86_64 #1 SMP Wed Mar 2 18:38:38 UTC 2022 
   RealMemory=1 AllocMem=0 FreeMem=179437 Sockets=36 Boards=1
   State=ALLOCATED+CLOUD ThreadsPerCore=1 TmpDisk=0 Weight=1 Owner=N/A MCS_label=N/A
   Partitions=queue 
...

NodeName=queue-dy-compute-resource-2 Arch=x86_64 CoresPerSocket=1 
   CPUAlloc=36 CPUTot=36 CPULoad=1.00
   AvailableFeatures=dynamic,c5n.18xlarge,compute-resource,efa
...

$ sstat 3
JobID         MaxVMSize  MaxVMSizeNode  MaxVMSizeTask  AveVMSize     MaxRSS MaxRSSNode MaxRSSTask     AveRSS MaxPages MaxPagesNode   MaxPagesTask   AvePages     MinCPU MinCPUNode MinCPUTask     AveCPU   NTasks AveCPUFreq ReqCPUFreqMin ReqCPUFreqMax ReqCPUFreqGov ConsumedEnergy  MaxDiskRead MaxDiskReadNode MaxDiskReadTask  AveDiskRead MaxDiskWrite MaxDiskWriteNode MaxDiskWriteTask AveDiskWrite TRESUsageInAve TRESUsageInMax TRESUsageInMaxNode TRESUsageInMaxTask TRESUsageInMin TRESUsageInMinNode TRESUsageInMinTask TRESUsageInTot TRESUsageOutAve TRESUsageOutMax TRESUsageOutMaxNode TRESUsageOutMaxTask TRESUsageOutMin TRESUsageOutMinNode TRESUsageOutMinTask TRESUsageOutTot 
------------ ---------- -------------- -------------- ---------- ---------- ---------- ---------- ---------- -------- ------------ -------------- ---------- ---------- ---------- ---------- ---------- -------- ---------- ------------- ------------- ------------- -------------- ------------ --------------- --------------- ------------ ------------ ---------------- ---------------- ------------ -------------- -------------- ------------------ ------------------ -------------- ------------------ ------------------ -------------- --------------- --------------- ------------------- ------------------- --------------- ------------------- ------------------- --------------- 
3.0                                                                                                                                                                                           213503982+        0          0       Unknown       Unknown       Unknown              0                                                                                                                                                                                                                                                                                                                                                                                                               
```

今度は何十分か経ってから終了しました．可視化ファイルも問題なく出力されています．

```shell
$ ls vis_out
mesh_vis_psf.0000.inp mesh_vis_psf.0002.inp mesh_vis_psf.0005.inp mesh_vis_psf.0009.inp
mesh_vis_psf.0001.inp mesh_vis_psf.0003.inp mesh_vis_psf.0008.inp mesh_vis_psf.0010.inp
```

out.logより，正常に実行・終了したことを確認します．

```:out.log
##################################################################
#                         FrontISTR                              #
##################################################################
---
version:      5.3
git_hash:     5db1d80452b951905658da828285c2fd0537603c
build:
  date:       2022-03-09T10:01:41+0000
  MPI:       "3.1, Intel MPI 2021.4.0"
  OpenMP:     201511
  option:    "-p --with-tools --with-metis --with-mumps --with-lapack --with-ml --with-parmetis --with-mkl "
  HECMW_METIS_VER: 5
execute:
  date:       2022-03-16T04:44:36+0000
  processes:  72
  threads:    1
  cores:      72
  MPI:       "3.1, Intel(R) MPI Library 2021.4 for Linux* OS"
  host:
    0: queue-dy-compute-resource-1
    1: queue-dy-compute-resource-1
    2: queue-dy-compute-resource-1
...
    70: queue-dy-compute-resource-2
    71: queue-dy-compute-resource-2
---
 fstr_setup: OK

 loading step=    1
 sub_step= 1,   current_time=  0.0000E+00, time_inc=  0.1800E+03
 loading_factor=    0.0000000   1.0000000
...
 iter:       5, residual: 9.5416E-01, disp.corr.: 6.2839E-04
### FSTR_SOLVE_NLGEOM FINISHED!

 ====================================
    TOTAL TIME (sec) :   4845.84
           pre (sec) :      1.29
         solve (sec) :   4844.55
 ====================================
 FrontISTR Completed !!
```

---

# 5. ちょっとした補足

- メッシュのサイズが大きい場合には領域分割もジョブとして投入する必要があります．ヘッドノードではメモリ不足になります．
- 子ノードがいつまでも確保できないなど，Slurmでエラーが発生した場合は，`/var/log/parallelcluster/slurm_resume.log`を参照します．中を見てはじめて，アベイラビリティーゾーン内に資源がないため失敗したことが分かった，という経験があります．

エラーについての参考↓

https://dev.classmethod.jp/articles/aws-parallelcluster-force-state-change/

---

- 前の記事: [FrontISTRをAWS ParallelCluster バージョン3で利用する②](https://qiita.com/chikasuiro/items/f95fdffc4e56e58fc27e)

[^2]: この辺りの詳細は[FrontISTRのマニュアル](https://manual.frontistr.com/ja/)をご覧ください．
[^3.3]: それぞれの状態(`ST`)ごとに0からカウントされます．実行時間はジョブ投入からの経過時間ではありません．
[^4.2]: https://github.com/aws/aws-parallelcluster/issues/1008