ANSYS MAPDLをpythonで自動化！pymapdlを利用してみた

# 目的
研究で実装したFEMモデルの精度を保証する為，ANSYSのMAPDLでの結果と比較する必要があった．  
そこで，MAPDLの計算をpythonでのパッケージpymapdlを用いて自動化させた．その時の技術を備忘録としてまとめていく．

# 参考文献
- [ansys-mapdl-coreのpypiでのドキュメント](https://pypi.org/project/ansys-mapdl-core/)
- [pymapdlのドキュメント](https://mapdldocs.pyansys.com/)

# pymapdl
pymapdlは，ANSYSが提供するMAPDLを，pythonを用いて実行することが出来るパッケージ．  

### インストール方法
pipを利用する場合は以下の通り．  

```
pip install ansys-mapdl-core
```

### APDLの起動方法
ANSYSが既にインストールされているPCにおいて起動するには*launch_mapdl*を利用する．  

```python
from ansys.mapdl.core import launch_mapdl
mapdl = launch_mapdl()
print(mapdl)
```

正常にインストール出来ているのであれば，ANSYSのパスなどは入力せずとも起動できる．  
成功した場合，次のように返してくる．  

```
Product:             ANSYS Mechanical Enterprise
MAPDL Version:       RELEASE  2020 R2           BUILD 20.2TEST  UPDATE 20200601
ansys.mapdl.core Version: 0.57.0
```

もし，ANSYSのパスを正常に認識できていない場合，次のようにパスを指定することで起動できるようになる．  

```python
from ansys.mapdl import core as pymapdl
new_path = 'C:\\Program Files\\ANSYS Inc\\v211\\ANSYS\\bin\\winx64\\ansys211.exe'
pymapdl.change_default_ansys_path(new_path)
```

また，リモートPCにあるANSYSを利用する場合，次のように使用するポート，そして対象のIPアドレスを入力すれば出来るとのこと（これは試していない）．

```python
from ansys.mapdl.core import Mapdl
ip = '127.0.0.1'
mapdl = Mapdl(ip=ip, port=50052, request_instance=False)
print(mapdl)
```

### 基本的な使用方法
MAPDLにおけるコマンドを，pymapdlでは次のように二つの方法で利用する方法がある．  
一つ目の方法はMAPDLのコマンドを*mapdl.run()*を使ってそのまま利用する方法だ．  

```python
mapdl.run('/PREP7')
mapdl.run('K, 1, 0, 0, 0')
mapdl.run('K, 2, 1, 0, 0')
mapdl.run('K, 3, 1, 1, 0')
mapdl.run('K, 4, 0, 1, 0')
mapdl.run('L, 1, 2')
mapdl.run('L, 2, 3')
mapdl.run('L, 3, 4')
mapdl.run('L, 4, 1')
mapdl.run('AL, 1, 2, 3, 4')
```

もう一つの方法は，mapdlに付属するモジュールを利用する方法だ．  

```python
mapdl.prep7()
# 節点部分の設定
mapdl.k(1, 0, 0)
mapdl.k(2, 5, 0)
mapdl.k(3, 5, 5)

# エッジ部分の設定
mapdl.l(1, 2, 1)
mapdl.l(2, 3, 1)
```

どちらでも良さそう．もしAPDLの方で既にコマンドを用意しているならば，一つ目の方法が楽そう．

### 終了方法
以下のコマンドを入力する．

```python
mapdl.exit()
```

単純にmapdl.finish()やmapdl.clear()を利用した場合，裏のプロセスにおいてMAPDLが残り続ける．  
この状態で，新しく*launch_mapdl*を利用した場合，ローカルの新しいポート番号に処理が割り当てられる（[launch_apdlのドキュメント](https://mapdldocs.pyansys.com/api/launcher.html)）．  
この状態を繰り返した場合，次のように開放済でないポート番号を利用してエラーを吐く恐れがある為，注意してほしい． 
 
```bash
OSError: Unable to connect to MAPDL gRPC instance at 127.0.0.1:50062
```

### 条件を変えて解析を実行させるコード例
ベルヌーイ・オイラー梁を利用した解析を行う場合  

```python
#nodes_pos:節点のx,y位置(np.array,(n,2))
#edges_indices:梁の節点組み合わせ(np.array,(n,2))
#edges_thickness:梁の太さ(np.array,(n))
#frozen_nodes:固定ノード(list,(m))
#input_nodes:入力ノード(list,(z))
#input_vectors:入力x,yベクトル(np.array,(n,2))

for i in range(10):
    nodes_pos, edges_indices, edges_thickness,\
        input_nodes, input_vectors, frozen_nodes = \
        make_random_fem_condition_with_continuous_graph()  # カスタムのランダムFEM条件生成関数
    # APDLの設定
    mapdl = launch_mapdl()
    mapdl.finish()
    mapdl.clear()
    mapdl.prep7()
    # 材料物性値の設定
    mapdl.et(1, 3)
    mapdl.mp("ex", 1, 1)  # ヤング率
    mapdl.mp("prxy", 1, 0.3)  # ポアソン比
    mapdl.mat(1)
    # 節点部分の設定
    for i, node_pos in enumerate(nodes_pos):
        mapdl.n(i + 1, node_pos[0], node_pos[1], 0)
    # エッジ部分の設定
    for i, edges_indice in enumerate(edges_indices):
        h = edges_thickness[i]
        mapdl.r(i + 1, h, (h * h**2) / 12, h, 0)  # A,I,height=y_length,SHEARZ
        mapdl.real(i + 1)
        mapdl.e(edges_indice[0] + 1, edges_indice[1] + 1)
    mapdl.finish()
    mapdl.run('/solu')

    # 解析条件設定
    mapdl.antype('static')

    # 固定ノード設定
    for i in frozen_nodes:
        mapdl.d(i + 1, "all", 0)
    # 外力設定
    for i, input_vector in enumerate(input_vectors):
        mapdl.f(input_nodes[i] + 1, "FX", input_vector[0])
        mapdl.f(input_nodes[i] + 1, "FY", input_vector[1])
    # 解析開始
    mapdl.solve()
    mapdl.finish()
    # 結果出力
    x_disp = mapdl.post_processing.nodal_displacement('X')
    y_disp = mapdl.post_processing.nodal_displacement('Y')
    z_rot = mapdl.post_processing.nodal_rotation('Z')
    ansys_disp = np.stack([x_disp, y_disp, z_rot]).T.flatten()

    print(ansys_disp)

    mapdl.exit()  # ポート番号などをリセットするために必要
```