Fortran について。
(一部 MacOS を想定)
Fortran は数値計算を主に取り扱うプログラミング言語である。歴史は古く、現在は数値計算の分野でしか使われないが、今でも物理などの分野では現役である (例えば Intel のコンパイラーは C/C++ と Fortran をサポートしている。)。実際長く使われている歴史があるので、信頼のおける言語である。また、言語構造が簡単なので、早く習得できると思う。
Fortran ももちろん、できてから今まで言語の更新が行われてきた。紙媒体に穴を空けたものからデータを読み取っていたときの名残が、Fortran 77 というバージョンには残っており、左を 6 マス空けなければならない。しかし、Fortran 90 や 95 といった後継の Fortran ではそれらは解消され、モジュールや、ポインターなども使えるようになり、今では現代的なプログラミング言語となっている。
目次
導入 (Mac)
Fortran を含め、多くの言語では、人間の目に易しい書き方が可能である一方、コンピュータからすれば非常に効率の悪い書き方になっている。なので、人間が書くときには人間に分かりやすく、コンピュータが実行するときにはより効率的な書き方にする必要がある。そのように変換することをコンパイルするという。コンパイルするツールをコンパイラーと呼び、Fortran ではこのコンパイラーが必要である。
環境準備
以下の手順でコンパイラーを導入する準備を行う。
- Xcode
を App Store からインストールする。
- 以下のコマンドで Command Line Tools をインストールする。
-
XQuartz
をインストールする。
-
MacPorts
または、
Homebrew
または、
Fink
などを用いて GNU の Fortran コンパイラーをインストールする。
ここでは、Homebrew の方法で試みる。
Homebrew のインストール
Homebrew のトップページにあるように、以下のコマンド (変更があり得るので確認すること) にて Homebrew をインストールする。
うまくいったら、以下のコマンドで最新状態にアップデートする。
さらにうまくいったら、以下のコマンドで問題がないかチェックする。
後々問題が起こらないように、ここで得られたエラーや警告は全て解決しておこう。
途中、ごちゃごちゃ言われたらその都度内容を読んで対処する。以下、起こりえる問題点を示す。
gfortran (GNU の Fortran コンパイラー) のインストール
gfortran は gcc のパッケージに含まれる。よって、以下のコマンドで、gcc をインストールする。
インストールが完了したら、
と実行してみて、
1 | gfortran: fatal error: no input files |
(インプットファイルが無いよ!) などと出力されたら、うまくいっている。
とすれば、コンパイラーのバージョンが分かる。
コンパイル
以下のようにプログラムファイル (test.f90) をコンパイルする。
すると、a.out というファイルができる。これを実行するには、
とする。ここで、a.out は実行ファイルと呼ばれる。
実行ファイルの名前を変えたければ、
1 | gfortran test .f90 -o test .exe |
とすればよい。実行ファイルとして、test.exe ができる。
基本的な書き方・ルール
まずは基本中の基本の話から
-
プログラムのファイル名は、xxx.f90 とする。
xxx の部分はアルファベット
[a-z,A-Z]
から始まり、アルファベット
[a-z,A-Z]
、数字
[0-9]
、およびアンダーバー
_
、ハイフン
-
、プラス記号
+
等が使える。あまり複雑な記号を使うのはエラーの元なので控える。空白文字 (スペース) は使わない。日本語も使わない。
-
Fortran 内部では、大文字と小文字の区別はない。パラメーターは大文字で書くなどの統一性を持たせると良い。
-
コメント文はびっくりマーク
!
で記述し、その行のそれ以降は無視される。
-
長いプログラム文は読みにくいので、適宜改行を入れる。改行を入れるには、
&
記号を行末に書く。また、分かりやすいように、改行後の行頭にも
&
を入れると良い。
1 | data = a + b + c + d + e + f + g + h & |
2 | & + i + j + k + l + m + n + o + p & |
3 | & + q + r + s + t + u + v + w + x & |
-
基本的に 1 コマンド 1 行だが、セミコロン
;
を使って複数のコマンドを 1 行に書くこともできる。
1 | a = 1 d 0 ; b = 2 d 0 ; c = 3 d 0 |
-
配列を使うことができる。配列とは変数名が一つで複数の変数を格納することができる箱。型名及び、次元を与える。
1 | integer , dimension ( 10 ) :: iarray |
3 | integer , dimension ( 2 : 10 ) :: narray |
4 | integer :: marray ( 1 : 10 , 0 : 9 ) |
-
文章の構成
-
プログラム名を書く。
-
明示的に型宣言をするように指定する。
-
変数を定義する。
型名::変数名
の形で定義する。パラメーターの場合は値も代入する。
-
パラメーター (定数) の定義。全て一般変数で定義してもいいが、パラメーター指定にすると、プログラムの実行速度が早くなり、また、バグの発見が容易になる。
-
一般変数の定義。
-
よく使う型は以下である。
型 |
記述方法 |
値の例 |
整数 |
integer |
1, 2, 5, 10 |
実数 (倍精度) |
real(kind(0d0)), couble precision |
1d0, 2.0d0, 5d-1 |
複素数 (倍精度) |
complex(kind(0d0)) |
cmplx(1.0,0.0,kind(0d0)), cmplx(0d0,1d0) |
文字 |
character(1), character(100), character(*) |
'abc', "xyz", "123" |
論理 |
logical |
.true., .false. |
-
変数には対応する形式で代入する。形式は、上の表の「値の例」参照。
-
変数名はアルファベットから始まれば何でも良いが、以下のルールをオススメする。
- 整数は、
i, j, n, m
を変数の前に付ける。
1 | integer :: imax , imin , jsite , num_elements , max_width |
- 実数は、
d
を変数の前につけdる。
1 | real ( kind ( 0 d 0 ) ) :: dval , data , dsize |
- 複素数は、
c
を変数の前に付ける。
1 | complex ( kind ( 0 d 0 ) ) :: cdata , cpsi , cphi |
- 文字列は、
s
を変数の前に付ける。
1 | character ( 10 ) :: sfile , sname |
- 論理型変数は、
l
を変数の前に付ける。
1 | logical :: lswitch , lflag |
- 実行するコマンドを書く。
- プログラムの終了を記述する。
プログラム例
04 | integer , parameter :: dp = kind ( 0 d 0 ) |
06 | complex ( dp ) , parameter :: zi = cmplx ( 0.0 , 1.0 , dp ) |
07 | real ( dp ) , parameter :: dpi = 3.1415926535897932d0 |
09 | integer , parameter :: iNx = 50 , iNy = 50 |
10 | real ( dp ) , parameter :: dxmax = 10 d 0 , dymax = 10 d 0 |
11 | real ( dp ) , parameter :: dxstep = dxmax / iNx |
12 | real ( dp ) , parameter :: dystep = dymax / iNy |
14 | real ( dp ) :: dphi ( iNx , iNy ) |
繰り返しの文
繰り返しの方法は、
do
と
enddo (end do)
を用いる。
i に 1 から 100 までを順番に入れて、それを足しあわせる例。
複数の入れ子構造も可能。
i にまず 1 を入れて、j に 1 から100 までを入れながらその合計を行い、終わったら、i に 2 を入れて、j に同じ操作、という感じに i に 100 が入って、j のループがなされるまで続ける例。
配列の値を操作する場合、Fortran では左側の変数から操作した方が処理が早い。
2 次元配列 darray の i, j 成分をどんどん足していく例。
05 | dval = dval + darray ( i , j ) |
12 | dval = dval + darray ( i , j ) |
条件分岐
結果によって処理を変えたいときには、if 文を用いて条件分岐を行う。
dval が 50 以上なら "pass" と書き出し、それ以外なら、"bad" と書き出す例。
dval が 80 以上なら "good" と書き出し、50 以上 80 未満なら "soso" と書き出し、50 未満なら "bad" と書き出す例。
3 | elseif ( dval > = 50 d 0 ) then |
書き出しのコマンド
-
DO 反復文
1 | print (A(i), i = 1, 100) |
ファイルへの書き出し
ファイルへの書き出しはまずはファイルを開く (作る)
open
コマンドから始める。
1 | open ( 10 , file = "test.txt" ) |
ここでは、10 番という装置番号で test.txt というファイルを開く (作る) ことを行っている。装置番号というのは
open
に関連付けられた番号で、
1 | write ( 10 , * ) 'this is a test.' |
とすれば、先ほど 10 番で開いた test.txt に書き込むことができる。
ファイルを開いたら、最後に必ず
close
文で閉じなければならない。
既存のファイルを開いて、その一番最後から追加で記述したい場合は、
open
にさらに
position = 'append'
という引数を追加する。
1 | open ( 10 , file = 'test.txt' , position = 'append' ) |
これで、再度
write
文で書き込みを行ったとき、ファイルの途中から書き込みを行ってくれる。
乱数の取り扱い (Intel MKL)
Intel コンパイラーを購入している場合、本格的に乱数を扱いたい場合や、テストで適当な乱数の配列を作りたい場合などに、組み込みの乱数発生装置ではなくて、Intel 謹製の乱数発生装置を用いることができる。その方法について述べる。詳しくはオフィシャルなドキュメントを参照のこと。
以下のようなモジュールを自分で準備して使うと便利。
11 | public randomnumber_ini , randomgenerate_gauss , randomgenerate_uniform |
13 | integer , save , private :: statvsl , irand |
14 | type ( vsl_stream_state ) , save , private :: planvsl |
15 | integer , parameter , private :: dp = kind ( 0 d 0 ) |
18 | subroutine randomnumber_ini |
20 | call system_clock ( irand , cr , cm ) |
21 | statvsl = vslnewstream ( planvsl , vsl_brng_mt 19937 , irand ) |
23 | end subroutine randomnumber_ini |
25 | subroutine randomgenerate_gauss ( inr , faverage , fsigma , frand ) |
26 | integer , intent ( in ) :: inr |
27 | real ( dp ) , intent ( in ) :: faverage , fsigma |
28 | real ( dp ) , intent ( out ) :: frand ( inr ) |
29 | statvsl = vdrnggaussian ( vsl_rng_method_gaussian_boxmuller , planvsl , inr , frand , faverage , fsigma ) |
31 | end subroutine randomgenerate_gauss |
33 | subroutine randomgenerate_uniform ( inr , fmin , fmax , frand ) |
34 | integer , intent ( in ) :: inr |
35 | real ( dp ) , intent ( in ) :: fmin , fmax |
36 | real ( dp ) , intent ( out ) :: frand ( inr ) |
37 | statvsl = vdrnguniform ( vsl_rng_method_uniform_std , planvsl , inr , frand , fmin , fmax ) |
39 | end subroutine randomgenerate_uniform |
41 | end module randomnumber |
上記モジュールをコンパイル時に読み込んで、使用したいプログラムの中で、
4 | integer , parameter :: ixsize = 128 , iysize = 128 , izsize = 128 |
5 | integer , parameter :: ivolume = ixsize * iysize * izsize |
6 | real ( dp ) , dimension ( ixsize , iysize , izsize ) :: dmatrix |
として、まずは初期化する。
それから、一様乱数なら、(範囲 [0,1])
1 | call randomgenerate_uniform ( ivolume , 0 d 0 , 1 d 0 , dmatrix ) |
とすれば良い。ガウス分布の乱数なら、(平均 0.5, 分散 0.2)
1 | call randomgenerate_gauss ( ivolume , 0.5d0 , 0.2d0 , dmatrix ) |
とする。
コード集