Fortran Advent Calendar 2019です。よろしくお願いします。
##はじめに
FortranユーザーにOOP(object-oriented programming)なFEMプログラムの良さを感じてもらえるきっかけになればいいなと思って書きました。
本記事ではOOPの恩恵を伝えきれてはいませんが(特に開発者側の恩恵)、ユーザーから見える部分でその良さについて解説できればと。
なお、今回想定しているユーザーは、本格的なプログラム開発まではせずモジュールの利用などにとどまる、といった感じです。
##HEC-MWを取り上げて説明してみました。
完全な模擬プログラムでもいいのですが、せっかくなので実在するプログラムを拝借して説明させてもらいます。
「HEC-MW」はオープンソースなFEMプログラムのライブラリで、メッシュの読み込み、行列方程式の求解、結果の可視化などの有限要素プログラムの基本的な機能が備わっています。オープンソースなFEMソフトウェアのFront-ISTRの基幹部分に組み込まれており、マニュアルはソフトウェアのリポジトリに入っているので、詳細が気になる方は参照ください。
https://gitlab.com/FrontISTR-Commons/FrontISTR/tree/master/hecmw1/doc
とはいえ、以下はあくまで模擬コードですので、ご留意ください、、、
###FEMプログラミングを始めます
HEC-MWを使って、力学解析をやるぞ!と思って書いたのが下記のmain1です(あくまで”つもり”です)。HEC-MWを使ってメッシュの読み込みや行列方程式の求解を行っています。剛性行列の組み立てなどのプログラムは自身で作成しますが、サブルーチン中ではHEC-MWを利用している、という感じです。
program main1
use hecmw
implicit none
type (hecmwST_local_mesh) :: hecMESH
type (hecmwST_matrix) :: hecMAT
character(len=HECMW_FILENAME_LEN) :: name_ID
call hecmw_init()
name_ID = "solid"
call hecmw_get_mesh(name_ID, hecMESH) ! メッシュの読み込み
call my_assembling_for_solid(hecMESH, hecMAT) ! ユーザーが自分で作ったサブルーチン(力学解析)。マトリックスを組んだりする。
call hecmw_solve_33(hecMESH, hecMAT) ! 行列ソルバ
call hecmw_finalize ()
end program main1
hecmwST_local_meshはメッシュ関係、hecmwST_matrixは行列方程式関係の構造体になっており、hecMESH, hecMATを定義して、わざわざそれをサブルーチンにインアウトさせているつくりになっています。どちらも構造体になっているのがポイントで、関係する変数が基本的にはすべて構造体の成分になっています。例えば、hecmwST_local_meshだとこんな感じ
type hecmwST_local_mesh
character(*) :: gridfile ! ファイル名
integer :: n_node !総節点数
real,allocatable :: node(:) !節点座標
...
...
end type
それぞれの変数を、いわゆる**"グローバル変数”で定義していない**点を強調させてください。
一方で、定常伝熱解析をやろうと思って書いたのが下のmain2です。ほぼmain1のコピペ。
program main2
use hecmw
implicit none
type (hecmwST_local_mesh) :: hecMESH
type (hecmwST_matrix) :: hecMAT
character(len=HECMW_FILENAME_LEN) :: name_ID
call hecmw_init()
name_ID = "heat"
call hecmw_get_mesh(name_ID, hecMESH) ! メッシュの読み込み
call my_assembling_for_heat(hecMESH, hecMAT) ! ユーザーが自分で作ったサブルーチン(伝熱解析)。マトリックスを組んだりする。
call hecmw_solve_33(hecMESH, hecMAT) ! 行列ソルバ
call hecmw_finalize ()
end program main2
###OOPの良さを考える
さて、こうなると連成解析をしたくなりますが、OOPな考え方でプログラムが用意されていれば、連成解析もスムーズです。
program main3
use hecmw
implicit none
type (hecmwST_local_mesh) :: hecMESHsolid, hecMESHheat
type (hecmwST_matrix) :: hecMATsolid, hecMATheat
character(len=HECMW_FILENAME_LEN) :: name_ID_solid, name_ID_heat
call hecmw_init()
name_ID_solid = "solid"
call hecmw_get_mesh(name_ID_solid, hecMESHsolid)
name_ID_heat = "heat"
call hecmw_get_mesh(name_ID_heat, hecMESHheat)
call my_assembling_for_heat(hecMESHheat, hecMATheat) ! ユーザーが自分で作ったサブルーチン(伝熱解析)。
call hecmw_solve_33(hecMESHheat, hecMATheat) ! 行列ソルバ
call my_assembling_for_solid_with_heat(hecMESHheat,hecMATheat,hecMESHsolid, hecMATsolid) ! ユーザーが自分で作ったサブルーチン。伝熱解析の結果を力学解析に反映させる。
call hecmw_solve_33(hecMESHsolid, hecMATsolid) ! 行列ソルバ
call hecmw_finalize ()
end program main3
ここで、hecMESH, hecMATとわざわざ別個に定義していたのがメリットになります。
力学解析用・伝熱解析用に別名(solidとheat)でメッシュを読み込み・定義することで、それぞれのメッシュであるhecMESHsolid, hecMESHheatを一つのプログラム中に共存させることができます。さらに、それぞれの異なる行列方程式(に関する変数)についてhecMATsolid, hecMATheatを共存させることができます。
また、hecmw_get_meshやhecmw_solve_33に注目すると、連成させる前に使っていたように変わらず使うことができています。
こうして、めでたくmain1, main2で書いたプログラムから大きな変更なくmain3が書ける、というのが感じられるでしょうか?
###OOPで考えるといいこと
今回連成が簡単にうまくいった大きな理由は、ユーザーで扱える変数で完結するということにあるでしょう。
たとえば、OOPを考えていないプログラムにおいてFEMの全節点数としてグローバル変数としたn_nodeなどがあったとしましょう。伝熱解析と力学解析で全く同じメッシュ・節点数を利用するのであればn_nodeを使ってもかまいませんが、発展的な内容で計算しようとすると(例えば、伝熱は四面体一次で力学は二次要素)わりかし面倒なことになりそうです。n_node_heatとn_node_solidを新たに定義するという道をとってしまうと、とも連れで修正が必要なサブルーチンがわんさか出てきて、もはや”利用”のレベルからは大きく逸脱してしまうでしょう。
一方で、先のプログラムではhecMESHsolid%n_nodeとhecMESHheat%n_nodeをそれぞれ別個に定義することができ、かつn_nodeの変数名が変わっているわけではないので同じサブルーチンを使いまわせることになります。
ユーザー側としてはかなり自由度高くプログラムを利用することができ、そして開発者側としても別途新しいプログラムを作る手間が減ります。
とてもありがたいですね!
##おわりに
といった便利なことができちゃうのがオブジェクト指向だ、ということをなんとなくユーザーから見えやすいところで紹介してみました。
この記事ではOOPのすべてを正確に伝えてきれてはいませんが、ざっくりとした理解の第一歩かと思うので、これを足掛かりにOOPなFortranプログラマが増えることを願うばかりです。
私自身も精進します、、、