Fortranの文法まとめ

•大文字と小文字は区別しない
•昔はすべて大文字で書くのが主流•予約語(PROGRAMなど)だけ大文字、あるいは文頭だけ大文字(Program)など色々なスタイルがある•授業で配布するサンプルはすべて小文字で作成・自由形式：1行132文字まで記述(コンパイルオプションで撤廃も可能) ・FORTRAN77では固定形式(何文字目から書くかが指定されていた) •一行に一文を記述• ;を使うと一行に複数文を書くことも可能•例）   a = 0; b = 0 ・スペースは無視される。 a=bもa = bも同じ。 ・!(エクスクラメーションマーク)以降の文字はコメント。コンパイル時には無視される・一文が長く複数行に分ける場合は&で次の行に続けて書ける。次の行頭にも&を置いてよい。 a = 1.0d0 + 2.0d0 + 3.0d0 + 4.0d0 + 5.0d0 & & + 6.0d0 + 7.0d0 + 8.0d0 + & &     9.0d0 + 10.0d0
プログラム名は英字から始まる適当な名前(FORTRAN77では6文字以内だったが90では31文字以内)
•変数(数値を格納する入れ物)には型がある• implicit noneは暗黙の型宣言を使用せず、すべての変数の型を明示的に指定する→必ず使用すること•暗黙の型宣言とは•古いFORTRANのコードではIMPLICIT REAL*8(a-h, o-z)と書き、 a～ｈまたはo～zから始まる変数→倍精度実数それ以外のi~nから始まる変数→デフォルトの整数と常に仮定。 •名前を間違えて打った時などに検出できないので非推奨•今でも整数はiから始まるものを慣習的に使うことがある(ierrorなど) •とりあえずprogram文を書いたら次の行にimplicit noneと書く
太字はよく使うもの。変数名：英数字とアンダーラインが使える。英字から始まる31文字までの名前
implicit noneの下で変数の宣言をすべて行う。宣言しない変数はプログラム中では使えない。
初期値を設定しない場合は::を省略できるが全部につけておけばよい。 1.23d4と書いた場合は1.23 × 104の倍精度実数、の意味。0.0d0は倍精度実数の0を表す。 1.23e4と書いた場合は1.23 × 10 4の単精度実数、の意味。0.0e0は単精度実数の0を表す。

・変数はコンピュータのメモリ上の保持されるため表現できる値に上限がある。
•整数型：4バイト(=32ビット) (1バイト=8ビット、1ビットは0か1の情報をもつ) ⇒2進数で32桁、正負の表現に1ビット使う(これは正確な表現ではなく負の数は補数を使って表す)ので最小値は-2³¹、最大値は2³¹-1 •単精度実数：4バイト、10-38～1038程度の範囲を約7桁の精度で表現→物理の計算では精度が不十分•倍精度実数：8バイト、10-308～10308程度の範囲を約16桁の精度で表現•単精度複素数型：8バイト、実部と虚部それぞれが単精度実数型•倍精度複素数型：16バイト、実部と虚部それぞれが倍精度実数型•論理型：値は真(.true.)か偽(.false.) 。T, FとしてもOK •文字型：1文字1バイト
倍精度実数型：8バイト＝64ビット1ビット符号11ビット指数部(e) 52ビット仮数部(b1, b2, b3, ..., b52)実数  = (-1)符号× (1 + b1 2-1 + b2 2-2 + b3 2-3 + ... + b52 2-52) × 2 (e-1023)この形式で近似されるため全ての実数値が表現できるわけではない
•数学の=(イコール)は左辺と右辺が等しいことを表すがプログラムの=は右辺の計算結果を左辺の変数に代入する、の意味•四則演算は+, -, *, /,累乗は**で表す。 ・同じ型同士の演算は同じ型になる。 •整数と実数の演算→実数、実数と複素数の演算→複素数
変数aに2を代入変数bに5を代入変数cにa + bを代入。この時点でaとbに値が入っているので2+5が計算されて変数cに7が代入される
順番を変えると結果が変わる。 cにa+bを代入。aとbに値がまだ入っていない。システム依存になるがおそらくaにもbにも初期値の0が入っているのでcもおそらく0となるaに2を代入bに5を代入cの値はその後変更していないので0のまま。数学的には同じに見えるがプログラムでは結果は異なる。プログラムを書くときは右辺に使う変数に値が入っているかを確認しながら書く

•標準入力(キーボードからの入力)

•標準出力(画面への出力) •標準エラー出力(画面への出力)
read, writeの１つ目の引数でファイル装置番号を指定する。*とすると標準入出力になるが、 Fortranでは5が標準入力、6が標準出力、0が標準エラー出力となっているシェル側でリダイレクトを使うことで標準入出力先をファイルなどに変更することができる計算した結果はwriteかprintで必ず出力する•実行文は上から一行ずつ実行するのが原則だがよく使う例外は2つだけ• doループ：do~end doで囲まれた領域をカウンタを変えて繰り返し実行

n
しを計算していることになっているi=1
増分値(ストライド)を1以外にするsum = 0 do i = 1, n, 3              !増分が3になる。i=1の次はi=4, i =7, ...となる。 sum = sum + i end do sum = 0 do i = 1, -10, -3          !増分が-3になる。i=1の次はi=-2, i =-5, ...となる。 sum = sum + i end do •配列とは：変数が複数並んだもの(メモリ上でも並んで配置される)。ベクトルや行列が表現できる。配列の宣言の基本double precision :: a(1:5)  ！aは5つの要素を持つ倍精度実数変数の配列a(1) a(2)
a(5) ←メモリ上にこのように5つの倍精度実数変数の領域が連続して確保される
配列の宣言例
!としてもよい。 double precision :: a(5) double precision, dimension(1:10) :: p, q, r, s  ! dimension属性を使うと同じサイズの配列を複数まとめて作れる。 ! p,q,r,sはすべて要素数が10の配列double precision :: p(1:10), q(1:10), r(1:10), s(1:10) !と同じdouble precision :: x(-2:2)！インデックスは1から始めなくてもよい。 ！この場合はx(-2) x(-1) x(0) x(1) x(2)の5つとなるdouble precision :: b(1:3,1:4)！ 3x4の2次元配列(行列)。b(3,4)としてもよい。
double precision ::  a(1:5) = 0.0d0    !配列aのすべての要素に0.0d0を代入integer :: k(1:5) = (/1, 2, 3, 4, 5/)       !配列kの各要素に初期値(順に1,2,3,4,5)を設定実行文として設定してもよい(こちらのほうが自由度が高い)。doループを使うdo i = 1, 5 a(i) = 0.0d0   ！ 5次元配列のすべての要素に0.0d0を代入end do異なる配列の違う添字を持つ要素の代入・演算も可能do i = 1, 5 a(i) = b(i+3)  ! a(1)=b(4), a(2)=b(5) ...のように代入されるend do
和C = sum(a(1:10)) ! a(1)からa(10)までの和をcに代入最大値・最小値d = maxval(a(1:n))  ! a(1)からa(n)の中の最大値をdに代入e = minval(a(1:n))    ! a(1)からa(n)の中の最小値をeに代入2つの1次元配列の内積| f = dot_product(a(1:n), b(1:n)) ！n要素の配列aとbの内積をfに代入a, bが複素数型の場合はaの複素共役とbの積が計算される
• 2次元配列で行列が表現できる
real*8, dimension(1:5,1:5) :: b do j = 1, 5 doi = 1, 5 b(i,j) = 0.0d0 end do end do行列の初期化二重ループ。この例では計算の順番はj=1, i=1 → j=1, i=2 → j=1, i=3 → j=1, i=4 → j=1, i=5 → j=2, i=1 → j=2, i=2 →   ・・・二重doループでは内側のループが先に変化する。 b(1:5,1:5) = 0.0d0  !0を代入するだけなら1行でもかける。 Fortranでの多次元配列はb(1,1) | b(2,1) b(3,1)
| b(4,2)の順でメモリ上に配置されるため(C言語では行・列が逆になる)doループは１つ目のインデックスを先に回すほうがメモリ上で連続的にアクセスできてよい(キャッシュミスが少なくなる)
real*8, dimension(1:5,1:5) :: a, b, c       !5x5の2次元配列(行列)としてa,b,cを宣言！a, bに値をここで代入(省略) do j = 1, 5 do i = 1, 5 c(i,j) = a(i,j) + b(i,j) end do end do do i = 1, 5 write(*,*) (c(i,j), j = 1, 5) ！cの各要素にa+bの値を代入! cの値を出力。 ! (c(i,j),j=1,5)はc(i,1), c(i,2), c(i,3),c(i,4), c(i,5)と展開されるend do n
ーつの要素を計算するのにdoループが必要real*8, dimension(1:5,1:5) :: amat, bmat, cmat    ! 5x5の行列を宣言! aとbの値を代入doj = 1, 5 doi = 1, 5 cmat(i,j) = 0.0d0         ! cの値を初期化するend do end do doj = 1, 5 doi = 1, 5 do k = 1, 5 cmat(i,j) = cmat(i,j) + amat(i,k) * bmat(k,j)    ! amatとbmatの積をcmatに代入。 end do end do end do
転置行列！bmatにamatの転置行列を代入bmat(1:n,1:n) = transpose(amat(1:n,1:n))行列積cmat(1:n,1:m) = matmul( amat(1:n,1:k), bmat(1:k,1:m)) ！(n×k行列のamatとk×m行列のbmatの積をcmatに代入(n×m行列) matmulは経験上計算速度が早くないので大行列の計算では他のライブラリを使うほうがよい
同じ値を複数の要素に代入する：doループで書くほか、一行でも書ける。 a(1:5) = 0.0d0 b(1:5,1:5) = 0.0d0 a = 0.0d0        !とすると配列aのすべての要素に0.0d0が代入される。 a(:) = 0.0d0  ！配列aの宣言されたすべての要素はこれでも指定できるa(:3) = 0.0d0 !とするとa(1)から(3)までに0.0d0が代入される同じ形状の配列に同じ代入を行うa(1:5) = b(1:5)     ! a(1)=b(1), a(2)=b(2), ...と同じことになる。 a(1:5) = b(4:8)       ! a(1)=b(4), a(2)=b(5), ...と同じ。要素数が左右で同じであればよいcmat(1:5,1:5) = amat(1:5,1:5) + bmat(1:5,1:5)  ！行列の和

•宣言時に配列の大きさを変数に使いたい
→配列より手前でparameter属性をつけて配列の大きさになる整数型を宣言。サブルーチンの場合はintent(in)属性をつける。 integer, parameter  ::  n = 100   ! parameter属性をつけるとプログラム中で値が変更できなくなるdouble precision :: a(1:n), b(1:n), c(1:2*n,1:2*n)
integer :: n real*8, dimension( : ), allocatable : a ！サイズを指定せずに配列を宣言read (*,*) n ！例えば実行時に標準入力からnの値を読み込むallocate (a(1:n))！配列の割り付け。aを要素数nの配列としてメモリに割り当てる！ここで計算をする(a(1:n)が使用可能) deallocate (a)      ! aの配列をメモリから解放する
• allocateはすでに割り付けられた配列に行うとエラーとなる•割り付けられていない配列をdeallocateするのもエラーとなる• allocated()関数は配列が割り付けられているかどうかの真偽を返す•特に割付配列をモジュール変数にした場合などに割付状況が不明になる。 if( .not. allocated(a) ) allocate (a(1:n)) !配列aが割り付けられていなければ割り付ける!これだと割り付けられていた場合前の情報が保持されるため、割り付けをリセットする場合はif(allocated(a)) deallocate(a)      !割り付けられている場合は解除する!割り付けるallocate( a(1:n)) •実行文は上から一行ずつ実行するのが原則だがよく使う例外は2つだけ• if文：条件を論理式で指定し条件を満たすかどうかで処理内容を変える
条件文
等しい時は=一つではなく2つなので注意論理型変数に論理式を代入できる。
論理式の値は真の時は.true. (またはT) 、偽の時は.false. (またはF)となる
•プログラムは最終行まで到達すれば終了•条件文を使うと途中で終了させることも可能• stop文で終了。 stop “文字列”とすると文字列が標準エラー出力に出力•プログラムの一番最後にstopと正常終了のメッセージを書く場合もある
integer :: delta, i, j read (*,*) i, j if (i == j) then delta = 1 else delta = 0 end if print *, delta integer :: delta, i, j read(*,*) i,j delta = 0 if(i==j) delta = 1 print *, delta
無限ループ, exit
exit文はdoループから抜け、end do文の直後に移動条件が満たされない場合は計算が終わらないため実行してみて終わらない場合は強制終了(C-c)するcycle: doループの先頭まで戻り、カウンタを次の値に進めるdoi = 1, n
do i = 1, n
doループが入れ子になっている場合cycleやexit文でどこに飛ぶのかがわかりにくい名前をつけない場合は一番内側のループに対して処理が行われるloop1: do i = 1, n
整数型↔実数型

組み込み関数sqrt(x)などでは引数は倍精度実数であるため、整数を引数に使いたい場合は先に実数型に変換する(sqrt(dble(i))など)
整数・実数型↔文字型
write(c,*) i     !整数型変数iを文字型変数cに変換read (c,*) i    !文字型変数cを整数型変数iに変換ファイル装置番号に文字型配列を使うことによって値の変換が可能整数の値を出力ファイル名に使うのに使うことがある値の代入z = (2.0d0, 3.0d0)    !倍精度複素数型変数zに2.0d0+3.0d0*iを代入する。実部と虚部が倍精度実数の変数a, bで与えられる場合iunit = (0.0d0, 1.0d0)  !虚数単位をプログラムの最初のほうで定義z = a + b * iunit !と書いてもよい。 z = dcmplx(a, b)実部・虚部の取り出しrez = dble(z)    ！複素数zの実部を倍精度実数型rezに代入imz = aimag(z)！複素数zの虚部を倍精度実数型imzに代入複素共役zconjg = conjg(z)  !組み込み関数conjgを使う
数学関数
引数は実数なので整数を使いたい場合は型変換で実数に変換したものをxに使う累乗は整数でも実数でもOK角度の単位はラジアン(180° = π rad)

数値関数

多くの関数には整数用・単精度実数用・倍精度実数用・単精度複素数用などの別名がある(abs(x)はiabs (整数→整数), abs(単精度実数→単精度実数), dabs(倍精度実数→倍精度実数) cabs(単精度複素数→単精度複素数), zabs(倍精度複素数→倍精度複素数)など。総称名のabsを使えば適切なものを呼び出してくれるため、dabsなどを明示的に使う必要はない。ただしcmplxは単精度複素数のみとなり倍精度複素数を使うにはdcmplxを使う。 realは単精度実数であるため倍精度実数ではdbleを明示的に使う。
• Fortranでは文字列の詳細な操作はしないほうがよい•文字列配列は主に入出力ファイル名として使う文字列の代入character(len=100) :: filename  ！100文字の文字列型変数filenameを宣言filename = ‘outputfile1.txt’ ! outputfile1.txtという文字列をfilenameに代入read (*,*) filename !または標準入力から読み込んだ文字列をfilenameに代入open(unit=10, file=filename, ... ) ! filename文字列で指定されるファイルを開くO u t p u t for in - | e | 1 | . | X t左詰めで配列に格納される。 filename(1:2)は’ou‘となる。余った部分は空白となる
•例：整数nを標準入力から読み込んでそれを使ったファイル名(outputfile1.txtの1の部分を変数nから読み込んだ値)を作りたいinteger :: n character(100) :: filename ! nの値を標準入力から読み込むread(*,*) n write(unit=filename, fmt=‘(“outputfile”, i3.3, “.dat” )’) n  ！ outputfile(3桁のn).datをfilenameに書き込むinteger :: n character(100) :: filename, filename1, filename2, nchar read(*,*) n ！整数型nを文字列ncharに変換する(右詰めで入る) write(nchar,*) n filename1=‘outputfile’ ！左詰めで代入filename2=‘.txt’ filename =  trim(filename1) // trim(adjustl(nchar)) // trim(filename2)！3つの文字列を結合する//で文字列の結合, trim()は文字列の右側の空白を削除する関数、adjustl()は文字列を左詰めにする関数
•関数：与えられた入力変数に対して操作をして値を返す一連の手続き•例えば4x³-5xを何度も様々なxに対して計算したいとする。プログラムのあちこちに4x³-5xと繰り返して書くとミスのもとになる。また後から関数形に変更が必要になったときにすべて探し出して変更する必要がある。 •関数副プログラムとしてf(x)=4x³-5xを計算する部分を独立させる。 •関数副プログラムを置く場所1.主プログラム(program ~end program)の中に置く(内部副プログラム) 2.主プログラムの外(end programの後)に置く(外部副プログラム) 3.モジュールの中に置く(モジュール副プログラム) •関数の引数にintent(in)属性を付けると関数内で値が変更できない(xの値は関数内では変更しない) • return文は関数やサブルーチンの処理を中断してその段階の値が関数やサブルーチンを呼び出したプログラムに戻される。プログラムの末尾の場合は省略できる
例1 ：主プログラムの中に関数副プログラムを置く場合(内部副プログラム) program example2 implicit none double precision :: x x = 0.0d0 do print *, x, func(x)     !ここでfunc(x)が呼び出される。関数はcontainsより下で定義されている。 x = x + 0.10d0 if( x > 5.0d0) exit end do ！contains以下に副プログラムを列挙するcontains function func(x)          !関数見出しdouble precision, intent(in) :: x  ！xを倍精度実数として宣言。intent(in)がつくと関数内で値を変更できない！関数の型を宣言する(倍精度実数型) double precision :: func func =  4.0d0*x**3 – 5.0d0*x ！関数を呼び出された元のプログラムに戻るreturn end function func end program example2
例2：主プログラムの外に関数副プログラムを置く場合(外部副プログラム) program example2 implicit none double precision :: x double precision :: func ！外部副プログラムは主プログラムから見えないので型宣言が必要x = 0.0d0 do print *, x, func(x)     !ここでfunc(x)が呼び出される。 x = x + 0.10d0 if( x > 5.0d0) exit end do end program example2 function func(x)           !関数見出しdouble precision, intent(in) :: x    ! xを倍精度実数として宣言。intent(in)をつけると関数内で値を変更できないdouble precision :: func ！関数の型を宣言する(倍精度実数型) func =  4.0d0*x**3 – 5.0d0*x ！関数を呼び出された元のプログラムに戻るreturn end function func
例3:モジュールの中に置く(モジュール副プログラム) ! programよりも手前にmoduleを配置。mod_funcは適当につけたモジュール名module mod_func ! module内でもimplicit none宣言implicit none contains ！containsより下にモジュール副プログラムを配置function func(x) !関数見出しdouble precision, intent(in) :: x    ! xを倍精度実数として宣言。intent(in)をつけると関数内で値を変更できないdouble precision :: func                  !関数の型を宣言する(倍精度実数型) func =  4.0d0*x**3 - 5.0d0*x ！関数を呼び出された元のプログラムに戻るreturn end function func end module mod_func program example2 use mod_func implicit none double precision :: x = 0.0d0 do print *, x, func(x)    !ここでfunc(x)が呼び出される。 x = x + 0.10d0 if( x > 5.0d0) exit end do end program example2
program test implicit none double precision :: a(1:3), b(1:3), c(1:3) a(1) = 6.0d0; a(2) = 3.0d0; a(3) = 4.0d0 b(1) = 3.0d0; b(2) = -2.0d0; b(3) = -4.0d0 c(1:3) = vectorproduct( a(1:3), b(1:3) )　！3要素の1次元配列型の関数write(*,*) c(1:3) contains function vectorproduct(a, b) double precision, intent(in) :: a(1:3),  b(1:3) double precision :: vectorproduct(1:3) ！関数の型を要素数3の1次元配列として宣言vectorproduct(1) = a(2)*b(3)-a(3)*b(2) vectorproduct(2) = a(3)*b(1)-a(1)*b(3) vectorproduct(3) = a(1)*b(2)-a(2)*b(1) return end function vectorproduct end program test
•再帰(recurrsion)とは：関数副プログラム(やサブルーチン)の中で自分自身(関数副プログラムやサブルーチン)を引用すること。 •再帰的に関数副プログラム(やサブルーチン)を呼び出す場合はrecursiveをつける。 •関数の実行結果は関数名ではなくresult句で指定された変数に代入される。フィボナッチ数列の例| recursive fuction fibonacci(n) result(fibo) integer, intent(in) :: n integer :: fibo ！結果を代入する変数if (n<0) then fibo = -1 return end if if (n == 0) then fibo = 0 else if (n == 1) then fibo = 1 else fibo = fibonacci(n-1) + fibonacci(n-2) ！自分自身を呼び出すend if return end function fibonacci
•関数副プログラムとほぼ同じ。違う点は•値を返さない(関数には型があるがサブルーチンにはない) •ただし引数として(複数の)値を返すことができる。 • call文で呼び出す•サブルーチン副プログラムは1つの実行文となるが、関数服プログラムは実行文ではない•長いプログラムを書く時は主プログラムに長々と書かずサブルーチンに分割する
program example2 implicit none double precision :: x, y x = 0.0d0 do ! call文でfuncサブルーチンを呼び出しcall func(x,y) print *, x, y x = x + 0.10d0 if( x > 5.0d0) exit end do ! contains以下に副プログラムを列挙するcontains subroutine func(x, y) implicit none double precision, intent(in) :: x        ! intent(in)がつくとサブルーチン内で値を変更できないdouble precision, intent(out) :: y     ! intent(out)がつくとサブルーチン内で値をセットしなければならないy =  4.0d0*x**3 – 5.0d0*x ！関数を呼び出された元のプログラムに戻るreturn end subroutine func end program example2
•サブルーチンの引数にはintent属性をつけることを推奨• intent(in) :入力引数、サブルーチンに値が渡され、サブルーチン内で値の変更ができない• intent(out) :出力変数、サブルーチンで値がセットされる変数• intent(inout) :入出力変数、サブルーチンに値が渡され、サブルーチンで値を変更できる変数•サブルーチン内だけで使う変数には何もつけない。
program test implicit none double precision :: a(1:3), b(1:3), c(1:3) a(1) = 6.0d0; a(2) = 3.0d0; a(3) = 4.0d0
end program test
•変数を主プログラムと副プログラム(関数やサブルーチン)で共有する
短いプログラムの場合は内部副プログラムが簡単に書けるが、内部副プログラムは独立性が低い(この副プログラムだけ他のコードにコピーしても使えない(a,b,cが中で未定義なので))
•基本は引数にすべて書く。引数に書かれていないものは参照できないprogram test implicit none real*8 :: x0, a=1.0d0, b=2.0d0, c=3.0d0, f   ! a,b,cの値は外部プログラムからは直接参照できないx0 = 1.0d0 print *, x0, f(x0,a,b,c) a = 2.0d0 print *, x0, f(x0,a,b,c) end program test function f(x,a,b,c) real*8, intent(in) :: x,a,b,c real*8 :: f f = a*x**2 + b*x + c return end function f外部プログラムは独立性が高い。計算に必要な変数がすべて引数に入っているので外部プログラムだけを他のコードで使うことができる。ただし引数が煩雑になる。
•モジュール内に変数を記述し、モジュールを参照する•引数には色々な値で計算することを想定されるものだけを入れるmodule func_mod !モジュールはprogramより手前に配置implicit none real*8, save :: a, b, c !副プログラムで使う変数をsave属性つきで定義!save属性をつけておくと前に代入された値を保持し続けるcontains function f(x)！モジュール副プログラムreal*8, intent(in) :: x real*8 :: f f = a*x**2 + b*x + c !モジュール副プログラムはモジュール内変数を参照できるreturn end function f end module func_mod program test use func_mod !implicit noneの手間でモジュールを参照implicit none real*8 :: x0 a=1.0d0; b=2.0d0; c=3.0d0 !モジュール変数に値をセットできるx0 = 1.0d0 print *, x0, f(x0) !モジュール関数も呼び出せるa = 2.0d0 print *, x0, f(x0) end program testモジュールは独立性が高い。モジュールの中で他のモジュールをuse文で参照することも可能module, programの順番に書く(別のファイルに分ける場合はこの順番にコンパイル)複数のmoduleがある場合は順番(依存関係)に注意
標準入力： read (*,*) var標準出力： write(*,*) varまたはprint *, var標準エラー出力： write(0,*) varシェル側でファイルに入出力シェルでリダイレクトによって標準入出力をファイルに変更Linuxのリダイレクトの部分を復習してください./a.out < inputfile > outputfile    # inputfileから標準入力を読み込み、標準出力をoutputfileに出力
Fortranでファイルを指定
open( 1, file='inputfile.txt', action='read')      !装置番号1としてinputfile.txtを開くopen( 2, file=‘outputfile.txt’, action=‘write’) ！装置番号2としてoutputfile.txtを開く!装置番号1(inputfile.txt)から読み込んだ値をvarに代入read(1,*) var ！装置番号2(outputfile.txt)に変数varの値を出力write(2,*) var !装置番号1のファイルを閉じるclose(1) !(この後装置番号1は他のファイルに当てることができる) !装置番号2のファイルを閉じるclose(2)装置番号5,6,0は標準入力、出力、エラー出力に予約されているのでこれ以外の番号を指定
• write,printで出力の書式指定ができる• writeの2つ目の引数、またはprintの*を書式に置き換える！ 5桁までの整数を表示write(*,'(i5)') ivar write(*,'(3f15.8)')   x, y, z ！実数を15桁で、小数点以下は8桁として3つ表示write(*,'(2i5,3f5.8)') i, j, x, y, z ！5桁までの整数を2つ表示、その後15桁,小数点以下8桁で実数を3つ表示
write(*,'(3x,i5)') 123 write(*,'(f8.2)') 123.4567 write(*,'(e8.2)') 123.4567 write(*,'(a8)') 'abc' _ _ 1 2 3 _ _ 1 2 3 . 4 5 _ 0 . 1 2 E + 3 _ _ _ _ A B C書式指定しない(*と書く)場合はデフォルトの書式が適用される(システム依存)
書式指定は文字列配列に代入したものを指定してもOK . • writeの指定子はunit(装置番号を指定)、fmt (書式を指定) •文字列操作で書式をプログラム実行中に決定したい場合に使えるreal*8 :: a character(50) :: format1 format1 = '(a, f15.5)' ！文字列配列format1に書式を代入write(unit=*, fmt=format1)  “a = “, a   !書式はformat1で指定する
•バイナリ入出力(バイナリ=2進数) •倍精度実数は10進数の形式(1.0000E+02など)で出力すると情報が落ちる。(メモリ上では64ビット、仮数部と指数部に分けて2進数で保持） •ファイル入出力するときに2進数(バイナリ)のまま扱うことができる•ただしテキストとして開いて読むことはできない(0と1が並んでいるだけでありバイナリエディタが必要だが読めたものではない)。バイナリファイルに出力するopen(1, file=‘output.txt’, form=‘unformatted’) !form指定子がunformattedだとバイナリ出力! unformatted(書式なし)の場合は書式なし出力write(1) var close(1)バイナリファイルから読み込むopen(2,file=‘output.txt’, form=‘unformatted’) ！開くときも同様!書式なしのread文read(2) var close(2)計算の途中経過出力、途中から再開などに使うまた図にする必要のないデータの保存などはバイナリで行う
•モジュール：変数、サブルーチン、関数などをまとめたもの•主プログラムなどからモジュールを参照し、中の変数、サブルーチン、関数などを使うことができる。 •大規模プログラムでは関連するものをモジュールとしてまとめることが多い•モジュール変数• parameter属性：例えば虚数単位など、主プログラムや関数・サブルーチンの様々な場所から参照する、値の変わらないものはparameter属性をつけてモジュール変数に入れておくとよい。 • save属性：モジュール変数にはsave属性を付けておくと主プログラム、副プログラムなど様々な場所から呼び出されたときに前回に代入された値が保存される。 • private属性：モジュールの外から変数の参照ができない•  public属性：(デフォルト)モジュールの外から変数の参照が可能•モジュールを参照するにはuse文で参照するモジュール名を指定•  useモジュール名, only: a, b, func •などとするとモジュールに含まれるa, b変数やfunc関数などのみが参照可能となる
・従来の方法：形状明示配列。副プログラムに値を渡すときに配列の大きさも引数に入れるmodule vec implicit none contains function vectoraddition(n,a,b) integer, intent(in) :: n  !配列のサイズをintent(in)属性で引数にいれるdouble precision, dimension(1:n) :: a, b !(intent(in)の値は指定できる) double precision, dimension(1:n) :: vectoraddition integer :: i do i = 1, n vectoraddition(i) = a(i) + b(i) end do return end function vectoraddition end module vec program array use vec implicit none real*8 :: a(1:3), b(1:3), c(1:3) a = (/ 1.0d0, 2.0d0, 4.0d0/) b = (/ -1.0d0, -3.0d0, 2.0d0/) c = vectoraddition(3,a,b)！aとbは3要素の配列print *, c(1:3) end program array
•形状引継ぎ配列：モジュール内の副プログラムで使用可能•配列のサイズを明示的に指定する必要がない•サイズが計算で必要な場合はsize関数で実行時に入手するmodule vec implicit none contains function vectoraddition(a,b) double precision, dimension(:), intent(in) :: a, b !形状引き継ぎ配列double precision, dimension(:), allocatable :: vectoraddition  !出力は割り付け配列integer :: i, n n = size(a,1)！サイズが必要な場合はsize関数(配列aの１つ目のインデックスの要素数) allocate(vectoraddition(n)) !出力の配列を割り付けるdo i = 1, n vectoraddition(i) = a(i) + b(i) end do return end function vectoraddition end module vec program array use vec implicit none real*8 :: a(1:3), b(1:3), c(1:3) a = (/ 1.0d0, 2.0d0, 4.0d0/) b = (/ -1.0d0, -3.0d0, 2.0d0/) c = vectoraddition(a,b) print *, c(1:3) end program array
• -oファイル名　：作成される実行ファイルの名前を指定• -O0, -O1, -O2, -O3  ：最適化オプション-O3が最も最適化されるが計算結果がおかしくなる可能性もある• -ffree-line-length-none ：一行132文字の制限を撤廃• -Wall ：全てのコンパイル時の警告メッセージを出力• -Wuninitialized ：初期化されずに使われた変数を検出• -pedantic ：標準外の機能利用を警告• -fbounds-check ：配列の領域外参照を検出• -ffpe-trap=invalid,zero,overflow浮動小数点例外発生時に異常終了• -fbacktrace ：異常終了時にプログラムソースコードの行番号を表示•  -c ：実行ファイルを作らずその手前のオブジェクトファイル(.o)のみ作成
•プログラムは一回で完全なものは書けない。間違い(バグ)が多くの場合ある。 •数値計算を行う研究もかなりの時間はバグ取り(デバッグ)に費やされる•コンパイル時にエラーが出て実行ファイルが作成されない•警告が出ても実行ファイルが作成される場合もある(-Wallをつけている場合など)のでls –lで実行ファイルと更新日時をチェック•エラーメッセージを見てバグを順番に取り除く•コンパイルは出来たが実行すると結果が明らかにおかしい•こちらは見つけるのが難しい•バグが見つけられない場合はどんどん質問してください。
エラーがどのファイルの何行目で出たかが出るので場所を確認するtest.f90:14:5:   → test.f90の14行目(5文字目)でエラー Error: Symbol ‘x’ at (1) has no IMPLICIT type
•変数の宣言し忘れ、変数名の打ち間違いはないか？ print *, b(4) 1 Warning: Array reference at (1) is out of bounds (4 > 3) in dimension 1 WarningであってErrorではないので実行ファイルはできるがこの配列の次元1のインデックスは3要素(b(1:3)で宣言)しかないのに4要素目を参照しようとしているという警告。このまま実行すると配列の領域外参照となる。コンパイル時に–fbounds-checkオプションをつけておくとエラーがでて終了になるがつけずに実行すると問題なく実行されることもある(結果がおかしくなる可能性が高い)
end function vectoraddition 1 Error: Expecting END DO statement at (1) end program array 1 Error: Expecting END IF statement at (1)コンパイラはend doやend ifが来ると思っているがその前に他のもの(end function/end program)が書いてある。 do, ifに対して(もっと手前で)end do, end ifを書き忘れていないか？ emacsではdo-end do等でTabキーを押すと字下げをしてくれるのでend do/end if書き忘れを見つけることができる。
print *, b(1) 1 Error: Invalid character in name at (1) printの手前に全角スペースを混ぜた場合有効ではない文字が混ざっているとエラーがでる(これは見つけにくい) print *, b(1,2) 1 Error: Rank mismatch in array reference at (1) (2/1) 1次元配列bを2次元配列のように２つインデックスを指定した場合配列のランクが合わないとエラーがでる。
program sin implicit none real*8 :: a a = 1.0d0 print *, sin(a) end program sin print *, sin(a) 1 Error: Symbol at (1) is not appropriate for an expressionプログラム名にプログラム中に使う組み込み関数と同じものを使うとコンパイル時にエラーとなるこの例ではsin(x)を使わなければコンパイルできるが、プログラム名にはできるだけ予約されている名前を使うのは避ける
•コンパイルは通ったが結果がおかしい場合、特にどこかで出力がNaN(Not a Number)またはInfinity(無限大)と表示される•負の数の平方根やゼロによる割り算などがおきていないか。 •ゼロで割っているつもりはなくても変数に値を入れ忘れると変数の値がゼロになっている場合がある。 ・ write文やprint文で割り算が行われる変数などの値を直前で出力して直に確かめる。 program error implicit none real*8 :: a, b a = 0.0d0 b = 10.0d0 print *, b/a, sqrt(-b)   !この実行結果は Infinity NaNとなるend program error
•コンパイルは通ってNaNやInfinityは出ないが計算結果がおかしい場合•そのプログラムで答えが既知の問題は解けないか？ •使っている関数を簡単なものにしたときに正しい答えが出せるか？ •手計算で答えが求められる場合に正しい答えを出すか？ •どの場合に結果が正しくてどの場合は結果がおかしくなるのかを調べてバゲの場所を特定する。
•書き直す時は削除せずに!でコメントアウトし、色々試すとよい。 •宣言された配列の領域外に値を代入すると他の変数の値を破壊する可能性がある。 •関数、サブルーチンの引数の順番、数、型などが合っているか？外部副プログラムの場合は特に整合性のチェックが入らないので実行時に結果がおかしくなる。 •色々な所にwrite,print文を入れてプログラムの動きをモニターする。 •特にdoループ, if文などが想定したとおりに動いているかをチェックする。doループではカウンタの値を毎回出力して想定通りに動いているか？if文では分岐それぞれにwrite文を入れて想定通りの分岐に入っているか？ •  read文で変数・配列に読み込んだデータは正しく読み込めているか？ •サブルーチンに渡す前の引数の値、サブルーチンに渡った直後の引数の値、サブルーチン実行後の引数の値など想定通りになっているか？ •コンパイルオプションを増やすと情報が得られる可能性がある。