使ってみませんか？−便利なソフト利用法−

Fortran コンパイラ「g95/gfortran」
核融合科学研究所
稲 垣

１．Fortran は実験データ解析に使えるか？

滋

まっていなければなりませんが，Fortran90 からは配列のFortran は定型のバッチ処理向き，というイメージが強大きさを実行時に変えることができるようになりました．

く，ダイナミックな要素の多い実験データ解析は不可能とこの機能を用いることで，配列の大きさをファイルやキー思われがちです．確かに FORTRAN77 では不可能かもしボード入力などで指定することができます．Fortran90 のれません．例えばファイルを開くまでデータの大きさがわ

動的配列は，C の malloc 系関数とは異なり多次元配列が確からない，しかもその大きさはファイルごとに変わり，上保でき，確保後は FORTRAN77 の静的配列と全く同様に限も未定であるような場合はどうやってコーディングすれ扱えます（その他の Fortran と C/C＋＋との違いは Appen-

ばよいでしょうか？しかし Fortran は FORTRAN77 から

dix１参照）．この動的配列の処理速度は，多くのコンパイ

Fortran90/95（最近 Fortran2003 の規格が発表されました）

ラで静的配列と同様です．実際，この動的配列を用いるだへと大幅な進化をとげ，最も現代的な言語へと成長しましけで今までの FORTRAN77 での煩わしさがかなり解消さた．ここでは Fortran９５の新機能を紹介して，実験データ

れ，コードの可読性が良くなります（配列のサイズを変え解析にも十分使えることを示したいと思います．

るためだけに再コンパイルする必要がない．下位のルーチ
ンの作業配列を上位のルーチンから渡す必要がないなど）．

２．Fortran95 での新機能プログラム例１は非常に単純な例です．自作と明記されてない関数は Fortran95 の組み込み関数です．最初の USE

私がよく使う Fortran95 の新機能を以下に示します．
１．

ダイナミックアロケーション．．．．というのはモジュール（後述）の呼び出しです．NUM

２．

配列演算

̲KINDS というモジュール（自作）に変数の精度が定義して３．

文字列操作関数

あり，今回は作業精度（wp）に倍精度（dp）を用い，整数

４．

モジュール，内部関数

は４バイト（i4b）としています．MYLIB にはいくつかの自

５．

構造体，ポインタ

作ルーチンが登録されており，そのなかの GET̲SIZE̲

このなかで，実験データ解析に欠かせないのが１のダイ

FROM と GET̲DATA̲FROM を使うことを宣言していま

ナミックアロケーションです．FORTRAN77 では，配列のす．コーディングスタイルに関しては http://www.mri-

サイズは作業用の配列を含めてすべてコンパイル時に決jma.go.jp/Project/mrinpd/coderule.html や Numerical Rec-

プログラム例１
PROGRAM PLOTDATA!
! コメントは ! で始まります．
! １行は１
３
２文字までです．
! 行頭を６文字空ける必要はありません．
! ７文字以上の変数名が使えます．
! 小文字が使えます．
!
USE NUM_KINDS, ONLY : wp=>dp, i4b! 自作
USE MYLIB,ONLY : GET_SIZE, GET_DATA! 自作!
IMPLICIT NONE ! 行の途中からもコメントが書けます．
! IMPLICIT NONE は必ずつけましょう!
INTEGER(i4b):: ntime, nx
REAL(wp), ALLOCATABLE :: time(:), x(:), Temp(:,:)!

３９８

J. Plasma Fusion Res. Vol.81, No.5 (2005)３
９８‐４０９

News of Related Fields! Fortran は変数，関数の大文字，小文字を区別しないので Temp, temp, TEMP は! 同義です．このため temporal のつもりで REAL(wp) :: temp という変数を新たに! 加えようとするとエラーが起こります．上記の Temp が温度としての意味ならば! temperature(:,:) と定義したほうが良いかもしれません．
!
CHARACTER(LEN=64) :: fname! CHARACTER*64 や filename*64 の書き方はやめましょう．
!
その他の宣言や前処理!
CALL GET_SIZE(fname, ntime, nx)! 自作
ALLOCATE(Temp(ntime, nx), time(ntime), x(nx))!
CALL GET_DATA(fname, time, x, Temp)! 自作!
データ処理，表示!
DEALLOCATE(time, x, Temp)!
END PROGRAM PLOTDATA

ipe in Fortran90 を参考にしています．Fortran90 の文法やALLOCATE したものは DEALLOCATE したほうが良い

FORTRAN77 との違いは http://www.ip.media.kyoto-u.ac.

と思います．上の例では配列の構築に GET̲SIZE̲FROM

jp/htomita/が参考になります（Fortran90 プログラミングと GET̲DATA̲FROM と２回手続きを行っています．こという書名で培風館より出版されています）．この例で重れは割り付け配列は宣言したルーチンで ALLOCATE しな

要なのは，ALLOCATABLE 宣言，ALLOCATE 文と DE-

ければならないからです．もし１回ですませたい場合は，ALLOCATE 文です．ALLOCATABLE で宣言された配列

モジュールでグローバル変数として管理するか，ポインタは割り付け配列と呼ばれ，ALLOCATE で確保し，DEAL-

を使います．割り付け配列は，主にメインルーチンでデー

LOCATE で開放することができます．Fortran95 では，使タ用の配列を確保するのに用いられます．サブルーチンでわれなくなった割り付け配列は自動的に開放されるので，作業用配列が動的に必要な場合は，もっと簡単な自動配列

DEALLOCATE はしなくても良い場合がありますが，DE-を用いることができます．プログラム例２に自動配列の例

ALLOCATE し忘れたときの保険程度に考え，基本的にはを示します．この例では coef と yfit が自動配列です．同じ

プログラム例２
SUBROUTINE PLOT_POLYFIT(x, y, ndeg)!
USE NUM_KINDS, ONLY : wp=>dp, i4b
USE LIBFIT,ONLY : POLYFIT! 自作!
IMPLICIT NONE!
INTEGER(i4b), INTENT(IN) :: ndeg
REAL(wp), INTENT(IN) :: x(:), y(:) ! 形状引継ぎ配列! 実際にこのルーチンを呼び出すときは形状引継ぎ配列を使用しているため明示的! インターフェイスが必要になる場合があります．
!
REAL(wp) :: coef(0:ndeg), yfit(SIZE(y)) ! 自動配列!
CALL POLYFIT(x, y, coef, yfit)
! 自作
!
データ表示
!
RETURNEND SUBROUTINE PLOT_POLYFIT

３９９

Journal of Plasma and Fusion Research Vol.81, No.5 May 2005

ことを割り付け配列でも実現できますが自動配列の方が簡紙に書いたアルゴリズムをほぼそのままコーディングでき

単（ALLOCATE，DEALLOCATE の必要がない）なうえ，

るようになり，バグが混入する危険性を少なくすることが配列の大きさによってはヒープ領域でなくスタック領域をできます（後述の並列化うんぬんより実はこの点が一番重使用します．上の例は FORTRAN77 とたいして違わない

要だと思います）．

ように見えますが，FORTRAN77 では ndeg と SIZE（y）が

配列演算は並列処理する仕様になっており，コンパイラ

コンパイル時に決まっていない場合はエラーになります．

や CPU がベクトル演算や並列処理をサポートしている場

ここまで Fortran の動的配列対応に触れてきましたが，合はパフォーマンスの向上が期待できます．ここで並列処

配列に密接に関連した機能である配列演算について説明し理に関して注意しなければならない点があります．

たいと思います．私が Fortran95 を使う最大の理由は，こindx(1:10)=(/ 1,2,3,4,5,6,7,8,9,10 /)の配列演算にあります．プログラム例３にいくつかの例をのとき示します．これに加えて Fortran95 では豊富な配列処理関indx(2:9) = indx(3:10)−indx(1:8)

数 SIZE ， SUM ， MINVAL ， MINLOC ， MATMUL ，とMERGE，PACK，TRANSPOSE な ど が 用 意 さ れ，コ ー

DO i = 2, 9

ディングがかなり簡単になりました．配列は FORTRAN

indx(i) = indx(i+1) − indx(i−1)

で発明されただけに，配列操作は Fortran に一日の長があ

ENDDO

る よ う に 感 じ ま す．こ の よ う に 配 列 演 算 を 用 い る と，で は 結 果 が 異 な り ま す．配 列 演 算 で は indx

Do-Loop がいらなくなりコードの行数が減るのに加えて，

1,2,2,2,2,2,2,2,2,10/)であり，Do-Loop では indx

プログラム例３○配列全体に０を代入
ex1: DO i = 1, SIZE(a)a(i) = 0.0_wp
ENDDO
ex2: a = 0.0_wp
ex3: a(:) = 0.0_wp
ex4: a(1:SIZE(a)) = 0.0_wp
上記４例は全く同じ結果となります．ex2-4 までが配列演算（代入）です．
書き方のスタイルは趣味の問題ですが，ex2 では a がスカラーか配列か（人間が）分かりにくいので，私は ex4 のような書き方をします．
○配列同士の足し算a(1:n) = b(1:n) + 3.0_wp*c(1:n)○配列要素同士の掛け算
a(1:n) = b(1:n)*c(1:n)○配列要素の基本数学関数演算a(1:n, 1:m) = SIN(a(1:n, 1:m)) + EXP(2.0_wp/b(1:n, 1:m))○偶数番目を０にする
a(2:n:2) = 0.0_wp○順番をひっくり返すa(1:n) = a(n:1:-1)○ある配列の 1,5,12,56,87 番目の要素を取り出したいindx(5) = (/1,5,12,56,87/)a(1:5) = b(indx)○絶対値が０より充分大きい時だけ要素の逆数を求める
WHERE(ABS(a) > SQRT(EPSILON(1._wp))) a = 1.0_wp/a○ベクトルの内積
DOT_PRODUCT(a, b)
これは SQRT(SUM(a*b))と同じ結果となります．

４００

=

(/

News of Related Fields= (/1,2,2,3,3,4,4,5,5,10/)となります．配列演算はマの位置を見つけたり，単位に括弧をつけたりなどの文字

一括 で処理されるのに対して，Do-Loop は 逐次 計算

列処理が必要になることが多いです．Fortran は，文字処が行われるので，i−1とi+1番目の要素を使って再定義さ理は苦手と思われがちですが，Fortran95 の文字列処理関れた i 番目の要素が，次のステップの i−1 で再び使われま

数は使いやすくパワフルです．プログラム例４に文字処理

す．配列演算は微分方程式を解くような場合に特に有効で関数の例を示します．この例の機能を用いれば，様々なす．

フォーマットのデータファイルに対応できます（しかし

実験データを解析する場合，数値を文字にしたり，コン

Fortran を用いるのであれば，データファイルのヘッダー

プログラム例４
CHARACTER(LEN=28) :: line
CHARACTER(LEN=12) :: key, strval
INTEGER(i4b):: max_len, ilen, ipos, ival!
line
= ’datasize = 128’
max_len = LEN(line)! max_len = 28
ilen
= LEN_TRIM(line)! ilen = 14
ipos
= INDEX(line, ’=’)
! ipos = 10
key
= line(1:ipos-1)! key = ’datasize ’strval = line(ipos+1:ilen)! strval = line(ipos+1:)でも良い
IF (.NOT. ISDIGIT(strval)) STOP
! strval に数字空白以外の文字が含まれていたら stop
READ(strval, *) ival
! ival = 128
ival = ival + 1
WRITE(strval, *) ival
! strval = ’ 129 ’line = TRIM(key)//’ = ’//&&ADJUSTL(strval)! line = ’datasize = 129’! 行は＆で連結できます．
line = TOUPPER(line)! line = ’DATASIZE = 129’!
! =================================================================!
FUNCTION ISDIGIT(str) RESULT(stat)!
! 入力文字 str が数字空白のみを含めば．TRUE.を，それ以外は．FALSE.を返す．
!
USE NUM_KINDS, ONLY : i4b!
IMPLICIT NONE!
CHARACTER(LEN=*), INTENT(IN) :: str
LOGICAL :: stat!
stat = .FALSE.
IF (LEN_TRIM(str) == 0) RETURN
IF (VERIFY(str, ” +-1234567890”) == 0) stat = .TRUE.
RETURNEND FUNCTION ISDIGIT!
! =================================================================!
FUNCTION TOUPPER（istr） RESULT（ostr）!
!
入力文字を大文字にします．TRIM した長さの文字を返すため!
明示的インターフェイスが必要になる場合があります．
!
USE NUM_KINDS, ONLY : i4b４０１

Journal of Plasma and Fusion Research Vol.81, No.5 May 2005!
IMPLICIT NONE!
CHARACTER(LEN=*), INTENT(IN) :: istr
CHARACTER(LEN=LEN_TRIM(istr)) :: ostr!
INTEGER(i4b), PARAMETER :: ascii_a = ICHAR(’a’) !
INTEGER(i4b), PARAMETER :: ascii_z = ICHAR(’z’) !
INTEGER(i4b):: i, ilen, ascii

= 97
= 127

!
ostr = ’’ilen = LEN_TRIM(istr)
IF (ilen == 0) RETURN!
DO i = 1, ilen
ascii = ICHAR(istr(i:i))
IF (ascii_a <= ascii .AND. ascii <= ascii_z) THEN
ascii = ascii - 32
ENDIF
ostr(i:i) = CHAR(ascii_code)
ENDDO
RETURNEND FUNCTION TOUPPER

や設定ファイルには NAMELIST を使う方が便利です）．

す．

Fortran95 の新機能であるダイナミックアロケーショ
ン，配列演算，文字列処理関数を用いれば，コードを書く

４．ライブラリという点では最近のインタプリタ言語並に簡単になったと

４．
１ グラフィックライブラリー
実験解析では実験データを可視化することが非常に重要思います．まだ変数宣言の手間などに違いがありますが，見つけることが非常に困です．Fortran には描画機能がないためライブラリを使う難なバグ［A３］の混入を防ぐために培われたノウハウなのことになります．ここで紹介するライブラリは Fortran かで，暗黙の型宣言（IMPLICIT REAL*8 (a-h, o-z)など）

ら呼び出して使うものです．

を使わず INPLICIT NONE を使い，がんばって変数宣言し・OpenGL の Fortran90 インターフェイス：http://math.

変数宣言と変数の型チェックは

nist.gov/f９
０gl

ましょう．

・広く普及している plplot: http://plplot.sourceforge.net

３．Fortran95 コンパイラ

・美しく高機能な DISLIN: http://www.mps.mpg.de/dislin

ここまでFortran95を薦めてきましたが，Fortranコンパ

私は g９５と相性が良く，Windows 版もある DISLIN を使っ

イラは高価です．しかしこの春 gcc4.0 の登場と共に g７７にています．Fig. 1 に DISLIN+g９５で作ったアプリケーショ

変わって gfortran が Fortran95 をサポートします（http:/ンの一例を示します．画面だけでなく ps や png フォーマッ/gcc.gnu.org/fortran/index.html）．また gcc ではありませ

トでも出力できるので論文やプレゼンテーションにも使えんがスタンドアローンの Fortran95 コンパイラとして g９５

そうです（当然ですが ps 出力の場合，図のような枠やボタ（http://www.g９5.org）があります．（実は g９５と gfortran

ンは含まれずグラフの部分だけになります）．通常の２次は元が同じで分岐したものです．）g９５の方が Fortran95

元グラフに加えて CONTOUR や SURFACE プロットもで標準への対応が少し進んでいるようで，私は Linux 版の g

きます．DISLIN には Perl 版などもありますが，Fortran９５を愛用してお り ま す．g９５は Windows 版 alpha 版 Mac

版はインタープリタ版と同様の手軽さで利用できます．

版もあるのでどんなプラットフォームでも試してみること

４．
２ 数値計算ライブラリができます．g９５のホームページには g９５でコンパイル可能

Fortran90/95 では，ほとんどの FORTRAN77 ライブラ

な様々なライブラリやコードへのリンク（http://www.g95.

リが使用可能です．しかしそのため Fortran95 ネイティブ

org/g95̲status.html）がありますが，その中でトカマクののライブラリはまだまだ少ないのが現状です．フォーマッ

乱流コード GYRO へのリンクがあるのは大変興味深いで

トや，古い FORTRAN77 の命令を新しいものに置き換え

す．日本の Open Source Project に対する寄与は海外に比べたりしたものは多くありますが，Fortran95 の並列処理をてまだまだ少ないと感じます．いつも恩恵にあずかるばか

活かしたり，最新のアルゴリズムを用いたものはいまのとりではなく例えば g９５を使ったコードやユーティリティをころ商用ライブラリに限られています．ここでは可搬性が開発して公開するなどの恩返しも必要なのではと思いま

高くほとんどのコンパイラでコンパイル可能と思われるフ
４０２

News of Related Fields

Fig. 1DISLIN を用いた実験データビューアの例（Imagemagic というアプリケーションで撮ったスクリーンショット）．DISLIN を用いれば通常のグラフのみならず，WIDGET や MENU を簡単に作ることができます．

リーフォーマット書式のものを紹介します．

５．モジュール

１：http://users.bigpond.net.au/amiller/非 常 に 多 く のFORTRAN77 ラ イ ブ ラ リ が Fortran90/95 の フ リ ー

今まで紹介してきた新機能のみでも十分なのですが，実

フォーマットに書き換わっています．

は Fortran95 の真髄はモジュールにあります．モジュール

２：http://www.pdas.com/fmm.htm 基 本 的 な 行 列 演 算，とは手続きとデ ー タ を パ ッ ケ ー ジ 化 し た も の で あ り，

スプライン補間，非線形方程式ソルバ，数値積分，常SUBROUTINE や FUNCTION よりも大きなプログラム単微分方程式ソルバのシンプルなパッケージ．ちょっと

位になります．今までのコーディングでは，あるまとまっ

Fortran95 を使ってみたい方にお手軽なライブラリとた処理をす る と き ど の よ う に SUBROUTINE に 分 け る

して適していると思います．その他のライブラリに関か，という設計を行いますが，Fortran95 ではまずどのよする情報はうな機能を持つモジュールが必要か，という設計を行いま

http://www.cts.com.au/tomasz.html

す．このモジュールの登場によりコードの保守，管理，再http://www.netlib.org/

利用が大幅に楽になりました．モジュールの機能のなかでhttp://www.personal.psu.edu/faculty/h/d/hdk/fortran.

最も単純で重要なものは，グローバル変数の管理です．

html

FORTRAN77 のコードで可読性を悪化させる最大の犯人などを参考にしてください．

は COMMON 文（と EQUIVALENCE 文）
です．これが IN４０３

Journal of Plasma and Fusion Research Vol.81, No.5 May 2005

CLUDE 文と一緒に使われるとソースを読むのがいやにもあり，このようなモジュールの有用性は増します．また，なってきます．反論があるかもしれませんが，Fortran95

このようなコンパイラや CPU による違いを吸収する機能

では COMMON 文と INCLUDE 文はできるだけ避け，モとして Fortran95 には EPSILON，TINY，RADIX，
MAXEX-

ジュールと USE を使ってコーディングするようにしま

PONENT などの数値モデル問い合わせ関数が用意されてしょう［A2］．以下にモジュールを使った例をいくつか示

います．これらを用いれば，より可搬性の高いコードを作します．最初にプログラム例５に既出の NUM̲KINDS のれます（LAPACK９５（http://www.netlib.org/lapack９５）で中身を示します．FORTRAN77 では，倍精度実数を宣言すもそのようにコーディングされています）．COMMON 文

るのに REAL（８）や REAL＊８や DOUBLE PRECISIONの代替としてのモジュール変数利用の例をプログラム例６を使ったりしますが，REAL（２）と指定するコンパイラもに示します．この例ではモジュールのもう一つの重要な機あります．また整数も INTEGER が４バイトだったり，８能であるスコープの限定の例にもなっています．モジューバイトだったりします．そのコンパイラ間の違いを吸収す

ル変数やモジュール手続きは USE 文によってスコープがるために Fortran95 では KIND，SELECTED̲INT̲KIND

限定できるため，名前空間の汚染が防げます（名前空間の関数が用意されています．NUM̲KINDSのようなモジュー

汚染とは例えばライブラリ A にGET̲DATAという関数がルを用意しておけば，コンパイラに依存せずに倍精度や４あり，ライブラリ B にも GET̲DATA という関数があった

バイト整数が指定できます．今後６
４bitCPU が一般化され，場合，コンパイラはライブラリ A とライブラリ B を同時に整数がデフォルト８バイトになったりすると，今まで単にはリンクできない）．このため自作した関数やサブルーチ

INTEGER だけで宣言してきたプログラムが壊れる可能性

ンはすべてモジュール化することを薦めます（明示的インプログラム例５
MODULE NUM_KINDSIMPLICIT NONE!
PRIVATE! モジュール変数はデフォルトで非公開とします．PRIVATE 宣言が無い場合や，! PUBLIC 宣言があればモジュール変数は公開です．
!
INTEGER, PARAMETER :: i4b = SELECTED_INT_KIND(9)INTEGER, PARAMETER :: i2b = SELECTED_INT_KIND(4)INTEGER, PARAMETER :: i1b = SELECTED_INT_KIND(2)INTEGER, PARAMETER :: sp = KIND(1.0)
INTEGER, PARAMETER :: dp = KIND(1.0D0)!
PUBLIC :: i4b, i2b, i1b, dp, sp! i4b, i2b, i1b, dp, sp を公開します．
END MODULE NUM_KINDS

プログラム例６
MODULE MYGLOBALUSE NUM_KINDS, ONLY : wp=>dp, i4b!
IMPLICIT NONE!
PRIVATE!
INTEGER(i4b), PARAMETER :: max_size = 1024*1024
INTEGER(i4b), PARAMETER :: max_num = 32!
INTEGER(i4b) :: data_size=0
INTEGER(i4b) :: channel_num = 0
INTEGER(i4b) :: rawdata(1:max_size, 1:max_num) = 0
REAL(wp):: time(1:max_size) = 0.0_wp!

４０４

News of Related Fields

PUBLIC :: data_size, channel_num
PUBLIC :: rawdata, time!
! max_size と max_num は公開(PUBLIC)されていないので他のサブルーチンからは! 使えません．
!
END MODULE MYGLOBAL!
! ==================================================================!
SUBROUTINE READ_RAWDATA(filename)
USE NUM_KINDS, ONLY : wp=>dp, i4b
USE MYGLOBAL, ONLY : datasize, channel_num, rawdata ! 自作
USE MYFILEIO, ONLY : NEWUNIT! 自作!
IMPLICIT NONE!
CHARACTER(LEN=*), INTENT(IN) :: filename!
INTEGER(i4b) :: uREAL(wp):: time! USE 文で time は宣言されていないので，この time は MYGLOBAL 内の time ではない．
!
u = NEWUNIT()
OPEN(u, TRIM(filename), STATUS=’OLD’)
ファイル操作!
READ(u,*) time
READ(u,*) datasize, channel_num
DO i = 1, data_size
READ(u,*) rawdata(i,1:channel_num)
ENDDO!
ファイル操作CLOSE(u)!
RETURNEND SUBROUTINE READ_RAWDATA!
! ------------------------------------------------------------------!
FUNCTION NEWUNIT() RESULT(u)USE NUM_KINDS, ONLY : wp=>dp, i4b!
IMPLICIT NONE!
INTEGER(i4b) :: u!
LOGICAL :: stat!
u = 10DO! 無限ループ
INQUIRE(UNIT=u, OPENED=stat)
IF (.NOT. stat) EXIT
! Do Loop は EXIT で抜けます
u=u+1
ENDDO
RETURNFUNCTION NEWUNIT

４０５

Journal of Plasma and Fusion Research Vol.81, No.5 May 2005

ターフェイスも不要になります）．以上はモジュールの最a(i,j) = REAL(i,wp)/REAL(j,wp)

も初歩的な使い方です．モジュールの機能は非常に大き(a(i,j) = i/j と書いてはいけません．）

く，演算子のオーバーロードなどの機能もあります．モ

END FORALL

ジュールと，構造体，ポインタを駆使することで C＋＋のク

は配列代入では書けません．今までのDO...END DOのルーラスに近い機能を実現することができます．Fortran95 でプを FORALL...END FORALL に変えることで明示的に並のオブジェクト指向プログラミングはまた機会があったら列処理ブロックを定義できます．上記例で PURE がついて紹介したいと思います．

いる関数MYFUNCは純関数と呼ばれ，副作用がありません．
Fortran95 の す べ て の 組 み 込 み 関 数 は 純 関 数 で す．

６．PC で数値シミュレーション

FORALL...END FORALL 内には純関数しか書けません．まFortran の本来の使命である処理速度に関しての進展にた純関数の一種で要素別（ELEMENTAL）関数というのも定ついて議論してみたいと思います．処理速度は言語によっ義できます．要素別関数ではスカラー引数に配列を渡すこて決まるものではなく，アーキテクチャ，アルゴリズム，とができます（戻り値も配列になります）．シングルスカ

コーディング，コンパイラの性能などによって決まります

ラープロセッサの PC では，明示的に並列処理を定義してが，ここでは言語の仕様とコンパイラの最適化について考もパフォーマンスの向上は期待できないかもしれません

えます．Fortran95 は並列処理を意識した唯一の言語です．

（CPU とコンパイラの組み合わせによっては擬似ベクトル

配列演算の項でも述べましたが，配列演算を用いた部分は化，擬似並列化ができるものもありますが）．しかし PC

並列処理可能であることがコンパイラには分かるので，コの CPU にも変化の兆しがあります．いまや CPU のクロッンパイラは最適化しやすくなります．Fortran95 には既出

ク速度は頭打ちで，今後はマルチコア化が進むそうです．

の配列演算以外にも並列化を意識した機能があります．

マルチコア CPU と Fortran95言語仕様は非常に相性が良い
気がします（コンパイラや OS が対応すればですが）．

DO i = 1, ny(i) = 5.0_wp*MYFUNC(x(i), pi) + &最後に& 3.0_wp

私が Fortran95 を薦めるのは，プラズマ核融合関連の

ENDDO

ユーザーが増え有用なライブラリやツールが公開されれ...

ば，今後自分の解析の幅が広がったり，開発効率が向上し

FUNCTION MYFUNC(x, a) RESULT(y)たりするのでは，という下心があるためです．実は資産の...
上の例では自作関数 MYFUNC に副作用があるか（x の値共有ができるのなら Fortran95 でなくても他の解析ツール

を変える，グローバル変数の値を変える等）どうか判別でのマクロやモジュールでも良いと思っています．ただ可搬きないので，実際は MYFUNC に副作用がなくてもこの性，汎用性，パフォーマンス，導入コスト，習得しやすさ，ループを並列化することは困難です．MYFUNCがFortran

そして最大の利点である 見つけるのが非常に困難なバグの組み込み関数の SIN などであれば，コンパイラは SIN［A3］の混入を比較的防ぎやすいという点で g95/gfortranに副作用がないことを知っているので，並列化してくれるが良いように思います．ツールは何であれ，資産が蓄積さかもしれません（くどいようですがコンパイラや CPU に依れ，それが他の分野でも有用であれば，それはプラズマ核

存します）．上記のルーチンを以下のように書き変えた場

融合分野のアクティビィティの高さを示すことにつながる合はどうなるでしょうかと思います．
今回例で示したソースのフルバージョンを公開します．

FORALL(i=1:n)

公開する以下のソースはすべてモジュールライブラリで

y(i) = 5.0_wp*MYFUNC(x(i), pi) + &す．

& 3.0_wp

nfnum.f９５

END FORALL

整数，実数の精度指定および pi などの定義．すべてのモジュールに必要．

...
nfutil.f９５

現時刻の所得やポインター関連のユーティ

FORALL 文によってコンパイラは並列処理可能であるこnfphys.f９５よく用いられる物理定数．

とがわかります．FORALL は並列演算を一般化したもの

nffio.f９
５

file 関連ユーティリティ．NEWUNIT，

PURE FUNCTION MYFUNC(x, a) RESULT(y)

リティ．

...

FINDFILE など

でハイパフォーマンスフォートランとの互換性のため導入
nfstr.f９５

されました．配列演算が暗黙的並列処理と呼ばれるのに対して FORALL は明示的並列処理と呼ばれます．すべての文字列処理ユーティリティ．TOUPPER，TOLOWER，DELCHAR など

配列演算は FORALL 文で表せますが，FORALL 文を配列
演算では表せない場合があります．

Appendixa(1:n−1) = a(2:n)*b(1:n−1)はA１

Fortran と C/C＋＋の違いは？

細かな違いはたくさんありますが，一番大きな点は C/

FORALL(i=1:n−1) a(i) = a(i+1)*b(i)と同義ですが

C＋＋は汎用，Fortran は科学技術計算用という設計思想でFORALL(i=1:n, j=1:m)
４０６

News of Related Fields

しょうか．本文でも述べましたが，Fortran は数式を使う

はCOMMON文には大きな問題があることを知っておかな

アルゴリズムをほぼそのままコーディングできるように，ければなりません．COMMON 文はなかなかエラーを出さという思想で設計されています．例えば配列要素の記憶順

ないため， 見つけるのが非常に困難なバグ の温床となり序が列順（数学の行列の表し方と同じ，これに対して C/やすいのです．

C＋＋は行順で，ファイルからのデータ読み込みに有利），

例えば main で COMMON/TEST/A，B と定義して多次元配列を動的に確保できる（C/C＋＋は１次元配列の

SUBROUTINE SUB1み），配列インデックスの上限下限が定義できる（C/C＋＋COMMON/TEST/B，Y

ではインデックスの開始は０で固定），複素数を言語としと書いてもエラーにはなりません．このコードを別の人がてサポートしている，などでしょう．これらに加えて言語

見た時，作者はわざとそうしているのか（もしそうなら一としての並列化のサポートなどがあります．コーディング

度お会いして理由を伺いたいものです），エラーなのかわのしやすさとは関係ありませんが，Fortran と C/C＋＋の思

かりません．またこの仕様のため COMMON/TEST/A，B

想の違いを示す面白い例がありますので紹介します．

の B がどこで変更されたかを追跡するのに，単純に B だけ

n=10;をサーチするだけでは安心できません．さらに COMMON

for (i=1;i<=n;i++){

BLOCK はすべてユーザーに公開されるので，ユーザー（多n−−;くの場合，半年後の作者）は，誤って変更したくない変数

printf(”%d¥n”,i);を変えてしまうかもしれません．例えば}COMMON/DATA2D/nx, ny, nz, X(100), &この結果は 1,2,3,4,5 となります．一方 Fortran では& Y(100), Z(100,100), nx_max, ny_max

n = 10という COMMON BLOCK があるとします．ここで，nx_max，

DO i = 1, nny_max は配列のサイズを表し，変えたくない（公開したくn=n−1ない）変数とします（COMMON BLOCK の変数は PARAME-WRITE(*,*) i

TER 属性を持てないためすべて変更可能）．この時 SUB-END DOROUTINE で nx だけを使いたいときでも，危険な変数 nx

この結果は 1,2,3,4,5,6,7,8,9,10 となります．For-_max，ny_max をユーザーに公開し な け れ ば な り ま せtran では Do-Loop が何回回るかは，Do-Loop に最初に入っん．さらにこの DATA2D という COMMON BLOCK には問題がたときに決まってしまうからです．ちなみにこのループをあります．もし配列のサイズを１００から２００に変えたい場合

抜けた後は i=11
（10 ではない），n=0 となっています．こはどうするのでしょう？すべての DATA2D を参照するサブのことから以下の例の結果も容易に想像できると思いま

ルーチンで変更作業を行わなければなりません．そしてもす．

し変更し忘れの個所があっても，多くの場合コンパイルエ
ラーになりません．従来，このようなバグを防ぐため COM-

C の例

MON 文は INCLUDE 文と共に使われてきました．しかし，実n=10;は INCLUDE 文は FORTRAN77 標準ではなく，その正式サ

for (i=1;i<=n;i++){ポートは Fortran90 からです．同じ Fortran90 標準を使う

if(i==4) i=n−1;のなら，スコープの限定ができ，変数の公開，非公開が管printf(”%d¥n”,i);

理できるモジュールを使ったほうが安全です．

}

グローバル変数はモジュールで管理しよう，と言っていFortran の例

ますが，なるべくグローバル変数を使わない方がコードの

n = 10

可読性は良くなります．グローバル変数が必要な場合はプ

DO i = 1, n

ログラムの設計が悪い場合がほとんどです．SUBROUTINE

IF (i==4) i=n−1の引数を少なくするためだけにグローバル変数を導入する

WRITE(*,*) iのには反対です．グローバル変数よりも，２０個も引数のあ

END DOる SUBROUTINE の方がまだ良いと思います（引数の数については Appendix４参照）．

C の例では結果は 1,2,3,9,10 となります．一方 Fortranではコンパイルエラー（Do-Loop 内ではその制御変数を変

A３

えてはいけない）になります．ループの最初でループ回数

見つけるのが非常に困難なバグとは？

コンパイルエラー，実行時エラーが出ない，結果は一見を決めてしまうので，途中で制御変数を変えられては困り正しそうだが良く見ると有効数字の３桁目以降が異なる，ます．納得！です．

またはある条件では間違っている．このような場合，バグ
なぜ COMMON 文を使ってはいけないのか？

を探し出すのは非常に困難です．よくある例ですが，ある

そもそも割り付け配列は COMMON BLOCK に入れるこ

インタプリタ言語で，実数のつもりの a に対して，a＝１ととができません．このため使おうと思っても自然と COM-してしまったため a が整数として扱われた（Fortran95 では

MON 文の出番は少なくなりますが，それでも使う場合に型不一致のエラーが出るか，警告を出すか，または実数に

A２

４０７

Journal of Plasma and Fusion Research Vol.81, No.5 May 2005

変換して計算する）．C で if(i==0){...とするところで使ってみて，Fortran が 発見困難なバグ の混入を最も防

if(i=0){...としてしまった（C では i＝０という代入演

ぎやすいと思います．

算は常に真を返す．Fortran95 ではコンパイルエラーが出
A４る）．FORTRAN で DO 10 I = 0. 10 としてしまったため，構造体はどんな時使うのか？

グローバル変数を使うぐらいなら２０個も引数のあるロケットの制御が不能になった（古い FORTRAN の仕様では空白が意味を持たなかったので Do-Loop の誤りではな

SUBROUTINE を使おう，と言いましたが，やはり２０個のく，DO10I という暗黙的に実数として宣言された変数に引数というのは少々使い勝手が悪いと思います．そんな0.1 を代入する，と解釈されてしまった（注：この逸話は

時，構造体を用いて関連した変数をパッケージ化すれ有名な作り話です）．Fortran95 ではコンパイルエラーが出ば，２０個の引数は数個の構造体で表されるでしょう．それますので心配いりません）．FORTRAN はその長い歴史ででは構造体の例を見てみましょう（プログラム例 A4）．こ

様々なバグと戦ってきました．そのノウハウが Fortran95

こではまだ POINTER の説明をしていないのですが，このの言語仕様に活かされています．私はいくつかの言語を例では割り付け配列とほぼ同義と思ってよいでしょう

プログラム例 A４! 構造体の宣言
TYPE ADCDATA
CHARACTER(LEN=64) :: diagnostic = ’’
! 計測器の名前，ECE など
INTEGER(i4b):: shotno=0! ショット番号
INTEGER(i4b):: data_length = 0! ADC の 1ch の取り込みデータ長
INTEGER(i4b):: channel_num = 0! 計測器の持つチャンネル数
REAL(wp):: t_sample = 0.0_wp! サンプリング時間
REAL(wp):: t_delay = 0.0_wp! データ取り込み開始時間
INTEGER(i4b), POINTER:: chans(:) => NULL()! 取り込みたい ADC のチャンネル番号．通常 chans = (/1,2,3,...,/)であるがチャンネル不良，! 増設，などで必ずしも１番から連続ではなくてもよい．その場合は! chans = (/3, 5, 12, 13, 14,...,/)のようになります．
REAL(wp),
POINTER :: param(:) => NULL()! チャンネルの持つパラメータ．そのチャンネルのエネルギーや計測位置．
! REAL ではなく CHARACTER のほうが汎用性は高いかもしれません．
REAL(wp),
POINTER :: time(:) => NULL()! 時間
REAL(wp),
POINTER :: volt(:,:) => NULL() ! 計測器の出力
END TYPE!
! --------------------------------------------------------------------------!
INTEGER(i4b), PARAMETER :: nplot = 8
TYPE(ADCDATA) :: plotdata(nplot)! 構造体配列を割り付け配列にする事も可能です．
REAL(wp) :: t_plot_begin, t_plot_end
REAL(wp) :: dummy
CHARACTER(LEN=128) :: dir!
plotdata(1:nplot)%shot = 12345
plotdata(1)%diagnostic = ”Ip”
plotdata(2)%diagnostic = ”Bolometer”
plotdata(3)%diagnostic = ”ECE”...
! 構造体のメンバーは％で参照します．
その他設定!
DO i= 1, nplot
CALL ADC_CHANNEL_SETUP(plotdata(i))! 自作
CALL ADC_GET_DATA(plotdata(i))! 自作
CALL ADC_CONV_TO_PHYS(plotdata(i))
! 自作

４０８

News of Related Fields

ENDO!
t_plot_begin = 0.0_wp
t_plot_end= 0.0_wp
DO i = 1, nplot
dummy = MINVAL(plotdata(i)%time)
t_plot_begin = MIN(t_plot_begin, dummy)
dummy = MAXVAL(plotdata(i)%time)
t_plot_end = MAX(t_plot_end, dummy)
ENDDO! 構造体のメンバーには通常の配列演算が行えます．
データの表示!
dir = ”/home/inagaki/＃１
２
３
４
５
DO i ＝ １， nplot
CALL ADC_SAVE(plotdata(i), dir)
! 自作
CALL ADC_CLEAR(plotdata(i))
! 自作
ENDDO

（割り付け配列は構造体のメンバーにはなれないのです．

...と個別に配列を用意しなければなりません．chans，

この問題は Fortran2003 では解決 し て お り g95/gfortranparam，volt についても同様です．これでは計測器を追加でも拡張として取り入れられています．しかし可搬性を考するとき非常に手間がかかります．構造体を使うことで引えるなら現時点では POINTER を使った方が良いと思いま

数が多くなるのを防げるうえに，コードの可読性，可搬性

す）．こ の 例 で 特 に 強 調 し た い の は，channel_num やがあがることがわかります．この例を見て C＋＋に詳しい

data_length が計測器ごとに異なる場合，構造体を使う方は おやっ と思われたかもしれませんが，例えば CALL

メリットは大きいということです．diagnostic などは構

ADC_GET_DATA(plotdata(i))の部分を C＋＋の plot-

造体を使わなくても diagnostic(nplot)と配列にすれdata[i].get_data()に 置 き 換 え て み る と，上 記 For-

ばよいですが，time については time(:,:)という２次元

tran のプログラムは C＋＋のクラスを用いたプログラムと配列を使ってもうまくいきません．C の「ディスプレイ」

良く似てきます（C＋＋では構造体はクラスの一種なの（別名ダブルポインタ，ポインタへのポインタ）のようなで，当たり前と言われれば当たり前なのですが．．
．
）．加え「異なる長さを持つ配列の配列」という機能が必要です．

て先ほどから強調しているモジュールによるスコープの管

Fortran では構造体以外に「ディスプレイ」を実現する方法

理，変数の隠蔽などの機能を用いれば，オブジェクト指向はありません．構造体を使わずに，上記例を実現させようのプログラミングもある程度可能になります．

とすると time_Ip(:), time_Bolo(:), time_ece(:),

４０９