【徹底解説】FrontISTRのfstr_mainにprintを追加してコンパイルする手順
こんにちは(@t_kun_kamakiri)。
FrontISTRはソースコードが公開されているので、自分で書き換えてコンパイルできます。とはいえ、いきなり難しい改造をすると「コンパイルのやり方が悪いのか、書き換えが悪いのか」が分からなくなりがちです。
そこでこの記事では、いちばん簡単な練習として 「print文を1つ差し込んで、コンパイルが成功したことを画面に出す」 をやってみます。あわせて、変数を定義して $a+b=c$($a=1, b=2$)を計算し、答えの3を表示させます。これができれば、FrontISTRの「書き換え → コンパイル → 実行」の一周を体験できます。
- FrontISTRのソースに1か所だけ print文を差し込む
- 変数を定義して $a+b=c$ を計算し、結果を表示する
- コンパイル(ビルド)して実行し、
コンパイル成功と1 + 2 = 3が出るのを確認する
FrontISTR 5.9 / WSL2環境に構築
この記事は、まず「編集する」「コンパイルする」「FrontISTRの設定ファイル」「コマンドを実行する」をとおして「書き換え → コンパイル → 実行」を一周します。そのあとの 詳しい解説 で、各操作が何をしているのかを1つずつ掘り下げる構成です。
- 使った環境
- 編集する
- コンパイルする
- FrontISTRの設定ファイル
- コマンドを実行する
- 詳しい解説
- 変数の宣言
subroutine fstr_main()- 「呼び出す」の意味
- C言語と同じ呼び出し規約(作法をそろえる)
- コンパイル後に名前が変わる問題(名前をそろえる)
implicit noneinteger(kind=kint) :: a, b, c- print文の記述
- HEC-MWの初期化とプロセス番号の取得
call hecmw_initmyrank = hecmw_comm_get_rank()nprocs = hecmw_comm_get_size()- 設定コマンドとコンパイルコマンドの違い
- 全ファイルがコンパイルされるのか、変更分だけか
- CMakeの設定コマンド
build_testフォルダの役割- コンパイルの実行
- 2回目以降のビルド(変更後の再ビルド)
- fstr_main() の全体像とステージごとのprint
- 元に戻す手順
- まとめ
使った環境
今回コンパイルした環境は次のとおりです。
| 項目 | 内容 |
|---|---|
| OS | Ubuntu 24.04(WSL2) |
| コンパイラ | GNU Fortran 13.3.0 |
| ビルドツール | CMake 3.28.3 |
| FrontISTR | バージョン5.9(Gitコミット 7f48eae0) |
FrontISTRのソースは、次の場所にあるものとします。
|
1 |
/home/kamakiri/src/FrontISTR |
このパスは、FrontISTRソースの /home/kamakiri/src/FrontISTR を起点とした相対パスです。フォルダ構成の中での位置は、次のようになっています。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
/home/kamakiri/src/FrontISTR/ ← FrontISTRソースの起点 ├── CMakeLists.txt ビルドルール ├── fistr1/ FrontISTR本体(構造・熱解析など) │ └── src/ │ ├── analysis/ 解析アルゴリズム │ ├── common/ 共通処理 │ ├── lib/ 要素剛性など計算ライブラリ │ └── main/ 起動まわり │ ├── main.c C言語の入口 main() │ └── fistr_main.f90 ★ 今回書き換えるファイル(fstr_main) ├── hecmw1/ 土台ライブラリ HEC-MW(メッシュ・並列・入出力) └── build_test/ ビルド作業用フォルダ |
fistr1/ がFrontISTR本体、hecmw1/ がそれを支える土台ライブラリ(HEC-MW)です。今回さわるのは fistr1/src/main/ の中の1ファイルだけです。なお build_test/ は最初から存在するわけではなく、あとのコンパイルで作られる作業用フォルダです(ここでは位置関係を示すために一緒に描いています)。
ここで、よく似た名前が2つ出てきます。混同しやすいので先に整理しておきます。
| 名前 | 何か | 綴り |
|---|---|---|
fistr_main.f90 | 書き換えるファイル名 | i が入る(fistr) |
fstr_main | そのファイルの中にあるサブルーチン名 | i が入らない(fstr) |
以降でも、末尾が .f90 ならファイル、そうでなければサブルーチンを指します。
編集する
ファイル fistr1/src/main/fistr_main.f90 を開き、その中の fstr_main サブルーチン(37行目あたりから始まります)の先頭に、2か所を書き足します。差し込む場所は「初期化が終わったあたり(hecmw_comm_get_size の直後)」です。
変数の宣言とprint文の追加
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
subroutine fstr_main() bind(C,NAME='fstr_main') implicit none real(kind=kreal) :: T1, T2, T3 integer(kind=kint) :: a, b, c ! コンパイル学習用に追加した変数 T1=0.0d0; T2=0.0d0; T3=0.0d0 ! =============== INITIALIZE =================== call hecmw_init myrank = hecmw_comm_get_rank() nprocs = hecmw_comm_get_size() ! ===== コンパイル学習用に差し込んだ print 文 ===== if( myrank == 0 ) then a = 1 b = 2 c = a + b print *, '===========================================' print *, ' Hello from customized FrontISTR!' print *, ' コンパイル成功 (compile succeeded)' print *, ' a + b = c ->', a, '+', b, '=', c print *, ' コンパイルの方法を学ぶためのブログ作成中! ' print *, '===========================================' endif |
integer の宣言や if( myrank == 0 )、print の文法は、後半の「編集したコードの詳しい解説」で1つずつ説明します。ここではこのまま書き足せば大丈夫です。
コンパイルする
はじめての場合(設定してからビルド):
|
1 2 3 4 5 6 7 8 9 10 |
cd /home/kamakiri/src/FrontISTR cmake -S . -B build_test \ -DCMAKE_BUILD_TYPE=RELEASE \ -DWITH_MPI=OFF -DWITH_MKL=OFF -DWITH_MUMPS=OFF \ -DWITH_METIS=OFF -DWITH_ML=OFF -DWITH_REFINER=OFF \ -DWITH_LAPACK=ON \ -DCMAKE_INSTALL_PREFIX=$HOME/local/frontistr cmake --build build_test -j2 |
最初の cmake -S . -B build_test ... は、ビルドの準備です。どのコンパイラを使うか、どの機能を使う/使わないか(-DWITH_MPI=OFF など)を決めて、build_test というフォルダに設定を書き出します。ここではまだコンパイルは始まりません。
上記コマンドを実行すると以下のようにログが出ると思います。


エラーが出ていないか確認します。
次の cmake --build build_test -j2 で、実際にコンパイルして実行ファイル fistr1 を作ります。初回は全部をコンパイルするので、少し時間がかかります。
一度ビルドしたあとは、ソースを直すたびに次だけでOKです。変更した部分だけコンパイルし直すので、すぐ終わります。
|
1 |
cmake --build build_test --target fistr1 -j2 |
上記コマンド実行時は以下のようなログが出ます。

FrontISTRの設定ファイル
ここからはテスト的にFrontISTRの設定ファイルを用意します。
先ほど追加したprint文が計算ログに出てくるかを確認するために、ダミーの設定ファイルを作成します。
解析条件 FistrModel.cnt:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
!VERSION 3 !WRITE,RESULT !SOLUTION,TYPE=STATIC !BOUNDARY fix, 1, 1, 0.0 fix, 2, 2, 0.0 fix, 3, 3, 0.0 !CLOAD force, 1, 100.0 !SOLVER,METHOD=DIRECT,ITERLOG=NO,TIMELOG=YES 10000, 1 1.0e-8, 1.0, 0.0 !MATERIAL, NAME=FC300 !ELASTIC, TYPE=ISOTROPIC 130000.0, 0.27 !DENSITY 7.4e-9 !EXPANSION_COEFF 1.2e-5 !END |
| ブロック | 意味 |
|---|---|
!NODE | 節点の座標。ここでは4点で四面体を作る |
!ELEMENT, TYPE=341 | 四面体1次要素(341)を1個定義(1, 1, 2, 3, 4 = 要素1が節点1・2・3・4からなる) |
!NGROUP, NGRP=fix / force | 固定する節点グループ(1〜3)/荷重をかける節点グループ(4) |
!MATERIAL / !SECTION | 材料の値と、それを要素グループ body に割り当てる指定 |
| ブロック | 意味 |
|---|---|
!SOLUTION,TYPE=STATIC | 静解析を行う |
!BOUNDARY | fix グループ(節点1〜3)を x・y・z 方向に固定(fix, 1, 1 は自由度1=x を固定の意味) |
!CLOAD | force グループ(節点4)の x方向に 100 N の集中荷重 |
!MATERIAL !ELASTIC | ヤング率 130000 MPa、ポアソン比 0.27。!DENSITY 密度、!EXPANSION_COEFF 線膨張係数 |
!SOLVER,METHOD=DIRECT | 連立方程式を直接法で解く |
単位系は mm-ton-s です(両ファイルの先頭にも # コメントで書いています)。
| 量 | 単位 |
|---|---|
| 長さ | mm |
| 力 | N |
| 応力・ヤング率 | MPa(= N/mm²) |
| 密度 | ton/mm³ |
| 時間 | s |
| 温度 | ℃(線膨張係数は 1/℃) |
なお、材料の値を .cnt と .msh の両方に書いた場合は、.cnt の値が優先されます。FrontISTRはメッシュ側の材料を先に読み込み、そのあと .cnt の !MATERIAL を同じ材料名(ここでは FC300)で照合して上書きするためです。今回はどちらも同じ値(E=130000 MPa など)にそろえています。
設定ファイルをEasyISTRで確認してみます。
固定条件は以下の3点をfixという名前のグループで設定しています。

荷重設定は以下の点をX方向100N設定しています。

コマンドを実行する
確認用モデルのフォルダへ移動して、作った fistr1 を実行します。
|
1 2 |
cd /mnt/d/work/002_CAE/frontistr/work/20260810_KinvH/model/007_compile_test /home/kamakiri/src/FrontISTR/build_test/fistr1/fistr1 |
1行目は、FrontISTRの入力ファイルがある解析フォルダへ移動しています。FrontISTRは通常、実行時の現在地から hecmw_ctrl.dat を探し、そこに書かれたメッシュや制御ファイルを読み込みます。そのため、解析設定ファイルのある 007_compile_test(※ここは任意) へ移動します。
2行目で、その場所から、別フォルダにあるコンパイル済みFrontISTRを起動しています。このパスは ~(ホームフォルダ)を使って ~/src/FrontISTR/build_test/fistr1/fistr1 と書いても同じです。手入力では ~ が短くて便利ですが、この記事ではどのフォルダを使ったか分かるよう絶対パスで書いています。
実行すると、計算のログの中に、書き足した表示が出てきます(### STAGE ... は後半の「fstr_main() の全体像」で足したものです。まだ足していなければ Hello 〜 の部分だけが出ます)。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
=========================================== Hello from customized FrontISTR! コンパイル成功 (compile succeeded) a + b = c -> 1 + 2 = 3 =========================================== ### STAGE 1: INITIALIZE (前処理・メッシュと条件の読み込み) ### Step control not defined! Using default step=1 fstr_setup: OK ### STAGE 2: ANALYSIS (solution_type = 1 ) ### ### Relative residual = 0.00000E+00 ### STAGE 3: FINALIZE (結果の後始末・ファイルを閉じる) ### FrontISTR Completed !! |
コンパイル成功 と a + b = c -> 1 + 2 = 3 が表示され、最後まで計算が進んで FrontISTR Completed !! で終わっています。これで、
- ソースを書き換える
- コンパイルする
- 実行して結果を確認する
という一周ができました。自分の書き換えが、ちゃんと実行ファイルに反映されていることも確認できます。
ちなみに今回の計算ケースで計算した結果をParaViewで確認するとこんな感じです。

特に意味のある解析結果ではありません。
fistr1(パスなし)とフルパスの違い
上では、わざわざ長いパスを打って実行しました。
|
1 |
/home/kamakiri/src/FrontISTR/build_test/fistr1/fistr1 |
たとえばfistr1(パスなし)コマンドで実行すると動くのはインストール済みの版です。
|
1 |
/home/kamakiri/local/frontistr/bin/fistr1 |
この時点では、fistr1とだけ入力すると、既にビルドしたインストール済みの版が実行されます。本記事でコンパイルした書き換え版は、まだインストール先に反映されていません。
build_testの書き換え版をインストールする
|
1 2 |
cd /home/kamakiri/src/FrontISTR cmake --install build_test |
このコマンドは、build_test に作成されたインストール設定を読み、コンパイル済みの build_test/fistr1/fistr1 を含む必要ファイルを /home/kamakiri/local/frontistr の下へ配置します。既に /home/kamakiri/local/frontistr/bin/fistr1 がある場合は、その実行ファイルが書き換えた版に更新されます。
これで ~/local/frontistr/bin/fistr1 が書き換えた版に置き換わり、以後は fistr1 と打つだけで本記事で書き足した表示が出ます。うまく置き換わったかは fistr1 -v の build: の date: で確認できます。
ただし1つ注意があります。インストールすると ~/local/frontistr/bin/fistr1 が上書きされるので、このシステムで fistr1 を使う他の作業も、すべて書き換えた版に切り替わります(計算結果は変わりませんが、書き足した表示が混ざります)。他の作業に影響させたくない場合は、インストールせず、これまでどおりフルパスで実行するか、別名を付けて使い分けます。たとえば次のようにエイリアスを作れば、fistr1(元の版)はそのままで、fistr1c で書き換えた版を呼べます。
|
1 |
alias fistr1c='/home/kamakiri/src/FrontISTR/build_test/fistr1/fistr1' |
この骨格の読み方は次のとおりです。
module m_fstr_main〜end module m_fstr_main… ファイル全体が1つの「モジュール」で包まれています。モジュールは、関連する変数と処理をひとまとめにする箱のようなものです。use ...(冒頭) … 他のモジュールで定義された変数や処理を借りてくる宣言です。これでkrealやhecmw_init、myrankなどをこのファイルの中で使えます。type(...) :: hecMESHなど … メッシュや行列といった、複数のサブルーチンで共有する大きなデータをここでまとめて宣言しています。saveは、処理が終わっても中身を保持する指定です。contains… これ以降が、このモジュールが持つサブルーチンの定義です。fstr_mainはこのcontainsの直後、先頭にあります。fstr_main以外のサブルーチン …fstr_init(初期化)やfstr_static_analysis(静解析)などです。fstr_mainがこれらを順に呼び出して解析を進めます。役割は後述の「fstr_main() の全体像」で改めて扱うので、ここでは「こういう部品が並んでいる」とだけ分かれば十分です。
今回さわるのは、この中の fstr_main の先頭部分だけです。
詳しい解説
変数の宣言
「編集する」で変数宣言として書き足したのは、次の1行でした。
|
1 2 3 4 |
subroutine fstr_main() bind(C,NAME='fstr_main') implicit none real(kind=kreal) :: T1, T2, T3 integer(kind=kint) :: a, b, c ! ← 追加:コンパイル学習用の変数 |
このあたりは、「fstr_main という処理の定義」と「その中で使う変数の宣言」に分かれます。順に見ていきます。
subroutine fstr_main()
subroutine は、Fortranでひとまとまりの処理を定義するキーワードです。fstr_main がその処理名です。
|
1 |
subroutine fstr_main() |
ざっくり言うと、これは このFortranサブルーチンを、C言語のプログラムから呼び出せるようにする ための指定です。そのしくみの1つとして、コンパイルのときに fstr_main という名前を(コンパイラに勝手に変えられないよう)そのまま付けてコンパイルさせています。この「名前をそのままにする」意味は、あとの「名前をそろえる」で詳しく説明します。
まず前提として、FrontISTRは1つのプログラムですが、中身は2つの言語を組み合わせて作られています。
- プログラムの入口(最初に動く部分)… C言語で書かれ、ファイルは
main.c。fistr1を実行したとき、いちばん最初に動くのがここです。-v(バージョン表示)などのオプションを読んだり、並列計算(MPI)を立ち上げたりといった、解析を始める前の準備を担当します。 - 解析の中身(計算の本体)… Fortranで書かれ、ファイルは
fistr_main.f90。 メッシュを読み込み、剛性行列を組み立てて方程式を解く、FEM計算の中心部分です。数値計算はFortranが得意なので、この部分はFortranで書かれています。
つまり、準備はC言語、計算はFortran、という役割分担です。そのため実行すると、まずC側の main()(main.c)が動き、準備が終わるとFortran側の fstr_main(fistr_main.f90)を呼び出します。
ところが、C言語とFortranは別々の言語なので、そのままでは互いの処理を呼び合えません。この「言語の壁」を越えて、C側からFortran側の fstr_main を呼べるようにするのが bind(C,NAME='fstr_main') です。指定は次の2つの部分に分かれます。
| 部分 | ざっくり何をするか |
|---|---|
bind(C) | 呼び出しの「作法」をC言語に合わせる |
NAME='fstr_main' | 呼び出すときの「名前」をC言語に合わせて fstr_main に固定する |
「作法」と「名前」の2つが合って初めて、C側からFortran側を呼べます。まず「呼び出す」とは何かを確認し、続けてこの2つをかみ砕いて説明します。
「呼び出す」の意味
プログラムでいう「呼び出す(呼ぶ)」とは、あるコードが、別のまとまった処理に「この仕事をやっておいて」と依頼し、実行してもらってから、元の場所に戻ってくることです。人に用事を頼むのに近いイメージです。
さきほどの役割分担でいうと、C言語の準備係が、自分の準備を終えたあとに「あとは解析をお願い」とFortranの計算係へ処理を渡します。この「処理を渡す」動作が、main.c に書かれた fstr_main(); という1行、つまり fstr_main の呼び出しです。だからFrontISTRを実行すると、C(準備)→ Fortran(計算)の順に処理が進みます。
「名前で呼ぶ」というのは、この依頼のときに 相手を名前で指名する ということです。main.c の fstr_main(); は、「fstr_main という名前の処理をやって」という指名になっています。だから、指名する名前(C側)と、指名される側の名前(Fortran側)が一致していないと、相手が見つからず呼び出せません。ここで問題になるのが、次に説明する「作法」と「名前」です。
C言語と同じ呼び出し規約(作法をそろえる)
呼び出し規約とは、ある処理を呼ぶときの「引数をどこに置いて渡すか」「戻り値をどう受け取るか」といった、細かい手順の取り決めのことです。
bind(C) を付けると、このFortranサブルーチンを 「C言語と同じ作法」で呼べる形 にしてくれます。これで、C側は普通のC関数を呼ぶのと同じ感覚で fstr_main を呼べるようになります。
コンパイル後に名前が変わる問題(名前をそろえる)
もう1つが名前です。まず前提として、私たちがソースに書いた fstr_main という名前は、コンパイル後のプログラムにそのままの文字で残るとは限りません。
コンパイルすると、ソースコードは機械語(コンピュータが直接実行できる形)に変換されます。このとき各サブルーチンには、プログラムの中でその処理を見つけ出すための「ラベル」が付きます。ラベルとは、いわば処理に貼られる名札です。さきほどの「名前で呼ぶ」で相手を探すときは、ソースに書いた名前ではなく、この名札(ラベル)を目印にして探します。
やっかいなことに、Fortranコンパイラは、この名札を作るときに、書いた名前そのままではなく、モジュール名などをくっつけた別の名前に作り変えることがあります。これを名前マングリングと呼びます。
たとえばGNU Fortranで、bind(C) を付けずに module m_fstr_main の中の fstr_main をコンパイルすると、ラベルは次のような名前になります。
|
1 |
__m_fstr_main_MOD_fstr_main ← 「モジュール名 m_fstr_main の中の fstr_main」という意味の名前 |
すると、C側が探す名前(fstr_main)と、Fortran側の実際のラベル(__m_fstr_main_MOD_fstr_main)が食い違います。この状態でプログラムを1つに結合(リンク)しようとすると、「fstr_main という名前が見つからない」というエラーになってしまいます。
そこで NAME='fstr_main' を付けると、Fortran側もラベルを作り変えず、書いたとおりの fstr_main のままにします。
|
1 2 |
bind(C) なし : __m_fstr_main_MOD_fstr_main ← C側の fstr_main と食い違う(つながらない) bind(C) あり : fstr_main ← C側の fstr_main と一致(つながる) |
これで、C側が呼ぶ fstr_main と、Fortran側のラベル fstr_main がぴったり一致し、無事に呼び出せるようになります。FrontISTRの起動部分(C側)は次のファイルにあります。
|
1 |
fistr1/src/main/main.c |
implicit none
Fortranには、宣言していない変数の型を、変数名の先頭文字から自動的に決める古い規則があります。implicit none はその自動判定を無効にし、使用する変数をすべて明示的に宣言させます。
|
1 |
implicit none |
この宣言は次のように分解できます。
| 部分 | 意味 |
|---|---|
real | 実数型 |
(kind=kreal) | FrontISTR共通の実数精度を使う |
:: | 型や属性と、変数名の区切り |
T1, T2, T3 | 宣言する3つの実数変数 |
FrontISTR 5.9では、kreal は次のファイルに定義されています。
|
1 |
hecmw1/src/common/hecmw_util_f.F90 |
fistr_main.f90 のモジュール先頭にある use hecmw がHEC-MWの定義を取り込んでいるため、fstr_main の中で kreal、kint、hecmw_init などを使えます。use は、他のFortranモジュールで公開されている定義を現在のモジュールから利用するためのキーワードです。
今回使用したGNU Fortranでは、real(kind=8) は通常、8バイトの倍精度実数に対応します。FrontISTRでは解析に使う実数の精度を kreal という名前で共通化しています。
T1、T2、T3 には、FrontISTRの初期化前後や解析後の時刻が入ります。最後に差を取り、前処理時間や解析時間を表示するための変数です。
integer(kind=kint) :: a, b, c
integer は整数型です。kind=kint は、FrontISTRで共通使用する整数の種類を指定しています。
|
1 |
integer(kind=kint) :: a, b, c |
今回使用したGNU Fortranでは、integer(kind=4) は通常、4バイトの整数に対応します。a、b、c は今回の練習で追加する3つの整数変数です。
kind の数値が常にバイト数を表すことはFortran規格全体で保証されていません。ここでは、今回のGNU Fortran環境とFrontISTRの定義に基づいて説明しています。
print文の記述
「編集する」でprint文として書き足したのは、次のまとまりでした。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
call hecmw_init myrank = hecmw_comm_get_rank() nprocs = hecmw_comm_get_size() ! ===== コンパイル学習用に差し込んだ print 文 ===== if( myrank == 0 ) then a = 1 b = 2 c = a + b print *, '===========================================' print *, ' Hello from customized FrontISTR!' print *, ' コンパイル成功 (compile succeeded)' print *, ' a + b = c ->', a, '+', b, '=', c print *, '===========================================' endif |
HEC-MWの初期化とプロセス番号の取得
print文の直前にある3行は、FrontISTRの基盤ライブラリであるHEC-MWを初期化し、並列実行の情報を取得する処理です。
|
1 2 3 |
call hecmw_init myrank = hecmw_comm_get_rank() nprocs = hecmw_comm_get_size() |
call hecmw_init
call はFortranのサブルーチンを呼び出すキーワードです。ここでは hecmw_init という初期化処理を呼んでいます。
hecmw_init の定義は次にあります。
|
1 |
hecmw1/src/common/hecmw_util_f.F90 |
hecmw_init の主な役割は次のとおりです。
- 既定の制御ファイル名を
hecmw_ctrl.datにする - HEC-MWの通信情報を初期化する
- MPI有効時は、MPIからプロセス数と自分のランクを取得する
hecmw_ctrl.datを読むための制御情報を初期化する
MPI有効版では、C側の main.c がそれより前に MPI_Init を実行しています。Fortran側の hecmw_init は、そのMPI実行環境からプロセス数とランクを取得し、HEC-MW内部で使えるようにします。
今回はCMake設定で -DWITH_MPI=OFF を指定し、MPI(並列計算)を使わない構成でビルドしています。この直列構成では、HEC-MW内部で次の値が設定されます。
|
1 2 |
プロセス数 = 1 自分のランク = 0 |
myrank = hecmw_comm_get_rank()
hecmw_comm_get_rank() は、現在このコードを実行しているプロセスの番号を返す関数です。戻り値を myrank に代入しています。
|
1 |
myrank = hecmw_comm_get_rank() |
ランク番号は0から始まります。例えば4プロセスで実行する場合、各プロセスの myrank は次のいずれかになります。
|
1 |
0, 1, 2, 3 |
たとえば、ランク0のプロセスでは myrank=0、ランク2のプロセスでは myrank=2 となります。各プロセスは、この番号で「自分がどの担当か」を識別します。
myrank は m_fstr.F90 でFrontISTR共通の整数変数として宣言されており、fistr_main.f90 では use m_fstr を通じて使っています。
nprocs = hecmw_comm_get_size()
hecmw_comm_get_size() は、並列実行に参加している全プロセス数を返す関数です。戻り値を nprocs に代入しています。
|
1 |
nprocs = hecmw_comm_get_size() |
例えば4プロセスで実行している場合、すべてのプロセスで nprocs=4 になります。それぞれの myrank は0、1、2、3と異なりますが、参加しているプロセスの合計は共通なので、nprocs はどのプロセスでも4です。
nprocs も m_fstr.F90 でFrontISTR共通の整数変数として宣言されています。
今回の WITH_MPI=OFF では、実行プロセスは1つだけなので、結果は常に次のようになります。
|
1 2 |
myrank = 0 nprocs = 1 |
そのため、続く次の条件は真になります。
|
1 |
if (myrank == 0) then |
MPIなしの今回は、実行中の唯一のプロセスがランク0です。したがって myrank == 0 を満たし、then から endif の間に書いたprint文が1回実行されます。
MPIを使っ4プロセス実行でも、この条件を満たすのは myrank=0 の1プロセスだけです。そのため、同じメッセージが4回重複せず、代表のプロセスから1回だけ表示されます。
この3つは別の処理です。最初の cmake -S ... -B ... だけでは、まだFrontISTR本体のコンパイルもインストールも行われません。
設定コマンドとコンパイルコマンドの違い
「コンパイルする」で使う2つのコマンドは、役割がはっきり分かれています。
| コマンド | 何をするか | 実行ファイルは |
|---|---|---|
cmake -S . -B build_test ...(設定) | CMakeLists.txt を読み、コンパイラやライブラリを調べ、-D... の設定を反映して、build_test に手順書(Makefile など)を作る。まだコンパイルしない | まだできない |
cmake --build build_test -j2(コンパイル) | build_test の手順書に従い、実際にソースを機械語へ変換し、結合して fistr1 を作る。-j2 は2つ並行 | ここでできる |
料理でたとえると、設定が「レシピと材料の準備」、コンパイルが「実際の調理」です。
全ファイルがコンパイルされるのか、変更分だけか
コンパイル(cmake --build)で毎回すべてを作り直すわけではありません。状況によって変わります。
- 初回(まだ何も作っていない状態)… FrontISTRの全ソースがコンパイルされます。ファイル数が多いので、数分〜十数分かかります。
- 2回目以降(ソースを1か所直しただけ)… 変更したファイルと、その影響を受ける部分だけが再コンパイルされ、最後に
fistr1を結合し直します。変えていないファイルは前回の結果を使い回すので、数十秒で終わります。
たとえば今回のように fistr_main.f90 を1つ直しただけなら、コンパイルし直されるのは基本的に fistr_main.f90 の分だけで、あとはリンク(結合)をやり直すだけです。
この「どこを直したか、どこに影響するか」の判定は、設定のときに build_test/CMakeFiles へ記録された依存関係にもとづいて、CMakeが自動で行います。私たちが「このファイルだけ」と指定する必要はありません。
CMakeの設定コマンド
|
1 2 3 4 5 6 7 8 |
cd /home/kamakiri/src/FrontISTR cmake -S . -B build_test \ -DCMAKE_BUILD_TYPE=RELEASE \ -DWITH_MPI=OFF -DWITH_MKL=OFF -DWITH_MUMPS=OFF \ -DWITH_METIS=OFF -DWITH_ML=OFF -DWITH_REFINER=OFF \ -DWITH_LAPACK=ON \ -DCMAKE_INSTALL_PREFIX=$HOME/local/frontistr |
各指定の意味は次のとおりです。
| 指定 | 意味 |
|---|---|
cmake | CMakeを起動する |
-S . | 現在のフォルダ . をソースフォルダとして読む |
-B build_test | build_test をビルド用フォルダとして使う |
-D変数名=値 | CMakeの設定変数に値を与える |
CMAKE_BUILD_TYPE=RELEASE | デバッグ用ではなく、最適化した実行用バイナリを作る |
WITH_MPI=OFF | MPI領域分割を使わない |
WITH_MKL=OFF | Intel MKLを使わない |
WITH_MUMPS=OFF | MUMPSソルバを使わない |
WITH_METIS=OFF | METISを使わない |
WITH_ML=OFF | MLプリコンディショナを使わない |
WITH_REFINER=OFF | メッシュ細分化機能をビルドしない |
WITH_LAPACK=ON | LAPACKを有効にする |
CMAKE_INSTALL_PREFIX=... | cmake --install を実行したときのコピー先を指定する |
$HOME はホームフォルダを表す環境変数です。今回の環境では、$HOME/local/frontistr は /home/kamakiri/local/frontistr と同じ意味です。
build_test フォルダの役割
build_test という名前に特別な意味はありません。build、build-release、build-test など、分かりやすい名前を任意に付けられます。今回は作業を区別するため build_test としています。
build_test がまだ存在しない場合は、CMakeが自動的に作成します。mkdir build_test を事前に実行する必要はありません。
設定後のフォルダには、おおむね次のものが入ります。
|
1 2 3 4 5 6 7 8 9 |
/home/kamakiri/src/FrontISTR/ ├── CMakeLists.txt FrontISTRのビルドルール ├── fistr1/ FrontISTRのソースコード ├── hecmw1/ HEC-MWのソースコード └── build_test/ コンパイル作業用 ├── CMakeCache.txt CMakeが記憶した設定 ├── CMakeFiles/ 依存関係などの作業ファイル ├── Makefile コンパイル手順 └── fistr1/fistr1 コンパイル後の実行ファイル |
ここに示したのは build_test 内の主な項目です。実際には、ライブラリやサブフォルダなど、FrontISTRのビルドに必要な他の生成物も入ります。
| 項目 | 種類 | 何が入っているか | 手で編集するか |
|---|---|---|---|
CMakeCache.txt | テキストファイル | CMakeの設定値と、コンパイラ・ライブラリの検出結果 | 通常は編集しない |
CMakeFiles/ | ディレクトリ | CMakeがビルドを管理するための内部情報 | 編集しない |
Makefile | テキストファイル | makeが読むビルド対象と処理手順 | 編集しない |
fistr1/fistr1 | 実行ファイル | FortranやCのソースをコンパイル・結合したFrontISTR本体 | 編集しない |
fistr1/fistr1 は、同じ名前が2回続いていて紛らわしいですが、次の意味です。
|
1 2 3 |
build_test/fistr1/fistr1 │ └─ 実行ファイル名 └─ fistr1用のビルド結果を置くディレクトリ |
これは、GNU FortranやGCCが各ソースをオブジェクトファイルへコンパイルし、リンカが結合して作った、Linuxで実行可能なバイナリファイルです。テキストエディタで開いてもFortranソースのようには読めません。動作を変えるときは fistr1/src/ 以下のソースを編集し、もう一度コンパイルします。
ソースとビルド中間ファイルを分けておくと、設定をやり直すときに build_test だけを作り直せばよく、FrontISTRのソースと混ざりません。また、設定の違うビルドを並べて保持することもできます(例:build-release/、build-debug/、build_test/)。
コンパイルの実行
設定ができたら、次でビルドします。
|
1 |
cmake --build build_test -j2 |
今回の指定では、完成品は /home/kamakiri/local/frontistr の下へ配置されます。したがって2つの場所の役割は次のように異なります。
| 場所 | 役割 |
|---|---|
/home/kamakiri/src/FrontISTR/build_test | CMakeの設定、コンパイル途中のファイル、コンパイル結果を置く作業場所 |
/home/kamakiri/local/frontistr | 完成したFrontISTRを日常の解析で使うためのインストール先 |
2回目以降のビルド(変更後の再ビルド)
一度ビルドしてある場合、ソースを書き換えたあとは fistr1 をビルド対象に指定して再ビルドできます。
|
1 2 |
cd /home/kamakiri/src/FrontISTR cmake --build build_test --target fistr1 -j2 |
cmake --build build_test…build_testに作成済みのビルド設定を使います。--target fistr1… CMakeが定義したfistr1というビルド対象を完成させます。ここでのfistr1はフォルダ名ではなく、実行ファイルfistr1を作るためのビルド対象名です。-j2… 2つのビルド処理を並行します。
CMakeが管理している依存関係により、変更したソースとその影響部分だけが再コンパイルされ、最後に fistr1 が作り直されます。「fistr1 フォルダだけをコンパイルする」という意味ではなく、「fistr1 を完成させるのに必要な範囲だけをビルドする」という意味です。
次の書き方も、今回のMakefile構成ではほぼ同じ処理です。
|
1 2 |
cd /home/kamakiri/src/FrontISTR/build_test make fistr1 |
make は build_test の中にCMakeが生成した Makefile を読みます。ただし、CMakeがMakefile以外のビルド方式を生成する環境でも同じ手順を使えるため、この記事では cmake --build ... --target fistr1 の書き方を基本とします。
fstr_main() の全体像とステージごとのprint
「編集する」では fstr_main の入口に1か所だけprintを差し込みました。ここでは一歩進んで、fstr_main が解析全体をどういう順番で進めているのかを眺めながら、その要所要所にprintを置いてみます。処理の流れが画面で追えるようになります。
fstr_main の役割(解析の司令塔)
fstr_main(fistr1/src/main/fistr_main.f90 の37行目〜)の中身は、コメントの区切りに沿って大きく3つのステージに分かれています。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
subroutine fstr_main() bind(C,NAME='fstr_main') ... ! =============== INITIALIZE =================== call hecmw_init ! 土台ライブラリ HEC-MW を初期化 myrank = hecmw_comm_get_rank() ! 自分のプロセス番号 nprocs = hecmw_comm_get_size() ! 全プロセス数 T1 = hecmw_Wtime() ! 時間計測スタート name_ID = 'fstrMSH' call hecmw_get_mesh( name_ID , hecMESH ) ! メッシュ(節点・要素)を読む call hecmw2fstr_mesh_conv( hecMESH ) ! FrontISTR内部形式へ変換 call fstr_init ! データ初期化+cntファイル読み込み call fstr_rcap_initialize( ... ) T2 = hecmw_Wtime() ! =============== ANALYSIS ===================== select case( fstrPR%solution_type ) ! 解析の種類で振り分け case( kstSTATIC ) ; call fstr_static_analysis ! 静解析 case( kstDYNAMIC ) ; call fstr_dynamic_analysis ! 動解析 case( kstEIGEN ) ; call fstr_eigen_analysis ! 固有値 case( kstHEAT ) ; call fstr_heat_analysis ! 熱伝導 case( kstSTATICEIGEN ) ; call fstr_static_eigen_analysis end select T3 = hecmw_Wtime() ! (ここで TOTAL / pre / solve の実行時間を表示) ! =============== FINALIZE ===================== call fstr_rcap_finalize( ... ) call fstr_finalize() ! メッセージファイルなどを閉じる call hecmw_dist_free(hecMESH) ! メッシュのメモリ解放 call hecmw_finalize if(...) write(*,*) 'FrontISTR Completed !!' end subroutine fstr_main |
3つのステージの役割は次のとおりです。
| ステージ | コメントの区切り | やっていること |
|---|---|---|
| STAGE 1 | INITIALIZE | HEC-MWの初期化、メッシュと解析条件(.cnt)の読み込み、データ構造の準備 |
| STAGE 2 | ANALYSIS | solution_type に応じて解析本体を呼ぶ。静解析なら K u = f を組み立てて解く |
| STAGE 3 | FINALIZE | 結果ファイルを閉じ、メモリを解放して終了。最後に FrontISTR Completed !! |
T1(開始)、T2(前処理おわり)、T3(解析おわり)で時刻を測り、あとで差を取って前処理時間・解析時間を表示しています。
select case の fstrPR%solution_type は、.cnt の !SOLUTION,TYPE= で決まる値です。今回の静解析は kstSTATIC(値は 1)なので、fstr_static_analysis に入ります。
各ステージへの print の追加
流れを目で追えるように、各ステージの入口に1行ずつ print を足します。すでにある「Hello / a+b=c」のブロックはそのままで、次の3行を追加します。
|
1 2 |
! (Hello / a+b=c のブロックの直後) if( myrank == 0 ) print *, '### STAGE 1: INITIALIZE (前処理・メッシュと条件の読み込み) ###' |
|
1 2 3 4 |
! =============== ANALYSIS ===================== if( myrank == 0 ) print *, '### STAGE 2: ANALYSIS (solution_type =', fstrPR%solution_type, ') ###' select case( fstrPR%solution_type ) |
|
1 2 3 4 |
! =============== FINALIZE ===================== if( myrank == 0 ) print *, '### STAGE 3: FINALIZE (結果の後始末・ファイルを閉じる) ###' call fstr_rcap_finalize( fstrPR, fstrCPL ) |
いずれも if( myrank == 0 ) を付けています。これは、並列(MPI)実行のときに全プロセスが同じ行を出すと画面が重複するので、代表の1プロセス(rank 0)だけが表示するためです。今回の WITH_MPI=OFF では常に myrank=0 なので、1回ずつ表示されます。
print *, '...', 変数, '...' のように、print の後ろはカンマ区切りで文字列と変数を混ぜて並べられます。STAGE 2 では fstrPR%solution_type の値もそのまま表示しています。
再ビルドと流れの確認
書き換えたら、これまでと同じように fistr1 を作り直します。
|
1 2 |
cd /home/kamakiri/src/FrontISTR cmake --build build_test --target fistr1 -j2 |
解析フォルダで実行すると、計算の進行に合わせて3つのステージが順番に表示されます。
|
1 2 3 4 5 6 7 8 9 |
=========================================== Hello from customized FrontISTR! コンパイル成功 (compile succeeded) a + b = c -> 1 + 2 = 3 =========================================== ### STAGE 1: INITIALIZE (前処理・メッシュと条件の読み込み) ### ### STAGE 2: ANALYSIS (solution_type = 1 ) ### ### STAGE 3: FINALIZE (結果の後始末・ファイルを閉じる) ### FrontISTR Completed !! |
solution_type = 1(静解析)に入り、INITIALIZE → ANALYSIS → FINALIZE の順に進んで、最後に FrontISTR Completed !! で終わっていることが分かります。
このように、「気になる場所の直前に print を置いて、実行時にそこを通ったか確かめる」 のは、ソースを読むときの基本のやり方です。どの関数がどの順番で呼ばれているのかを確かめながら読むと、大きなプログラムでも迷子になりにくくなります。
元に戻す手順
練習が終わって元のFrontISTRに戻したいときは、書き足したprint文と変数宣言をソースから削除し、もう一度 fistr1 をビルドします。
まず変更内容を確認します。
|
1 2 |
cd /home/kamakiri/src/FrontISTR git diff -- fistr1/src/main/fistr_main.f90 |
このファイルに今回の練習以外の変更がないことを確認した上で、書き足した行をエディタで削除します。その後、次で作り直します。
|
1 2 |
cd /home/kamakiri/src/FrontISTR cmake --build build_test --target fistr1 -j2 |
もし練習前の状態にそのまま戻したいだけなら、git で1つのファイルを元に戻すのが簡単です。
|
1 2 3 |
cd /home/kamakiri/src/FrontISTR git checkout fistr1/src/main/fistr_main.f90 cmake --build build_test --target fistr1 -j2 |
まとめ
- FrontISTRは
fistr1/src/main/fistr_main.f90のfstr_mainに print文を書き足すだけで、実行時に好きな表示を出せます。 - ビルドはCMakeで行い、初回は
cmake -S . -B build_test ...で設定してからcmake --build build_test -j2でコンパイルします。 - 2回目以降は
cmake --build build_test --target fistr1 -j2とすると、変更したファイルと依存部分だけが自動判定されて再ビルドされます。 fistr1(パスなし)は PATH上の元の版、フルパスのbuild_test/fistr1/fistr1は書き換えた版を指します。書き換えた版をfistr1で使いたいときはcmake --install build_testでインストールします。fstr_mainは INITIALIZE → ANALYSIS → FINALIZE の3ステージで進みます。各ステージの頭に print を置くと、実行時にどこを通っているかを目で追えます。
この「書き換え → コンパイル → 実行」の流れが分かれば、次はもっと意味のある改造(たとえば温度荷重行列Hを出力する DUMPH=YES の追加など。こちらの内容は後日ブログにアップします)にも進めます。

