@zigen 's note

Fortran Memo

最終更新:

mynote

- view
だれでも歓迎! 編集

Fortran




FFTプログラム

k-spaceを見るときフーリエ変換を用いるが、普通にフーリエ変換をしていくと何日かかるかわからない。そこで使用するのがFFT(Fast Fourier Transform=高速フーリエ変換)である!
数値計算結果の解析を行う上で数多あるFFTの内1つぐらいはFFTライブラリを使用できるべきであろうと思う。

以下簡単な使い方


Gnu fortran77 コンパイルオプション

$g77 -O3 -ml -o acip.out -g -Wall acip.f
  • O3 最適化オプション 「Option Level 3」の意味
  • ml Mathe libraryの使用(C言語やSin等の関数を仕様するとき必要かも)
  • Wall -Wunused と -Wuninitialized オプションの結合(Wから始まるオプションを全てと言う意味)

Fortranのデータ型

Integer型10桁 整数(4Byte)だから2^(4*8)で+ーある訳だから/2が最大値format文はI10で
2147483647(0を含む)~-2147483648

「'」「"」シングルクォーテーションとダブルクォーテーション

print文などで文字列を扱う場合には、文字列をシングルクォーテーション( ' ) またはダブルクォーテーション( " )でくくります。シングルクォーテーションでくくるかダブルクォーテーションでくくるかでは多少意味が異なります。シングルクォーテーション( ' )でくくった場合は、そのままの文字として扱われます。

例) print 'そのまま\n出力します';->[表示]そのまま\n出力します
ダブルクォーテーション( " )でくくった場合は、メタ文字(\+アルファベットで制御コードなどを表す文字)や 変数等を挿入して扱う事ができるようになります。
例) print "メタ文字を\n出力します";->[表示]メタ文字を(ここに改行コードが入る)出力します
状況に応じて使い分けるしかないのですが、全角の表示でShiftJISを使用する場合に、 「ソ」や「表」といった文字は2バイト目が \ と同じコードとなり、文字が化ける場合が あるため、全角の表示はシングルクォーテーション( ' )でくくる方が無難です。

Fortran90の2連コロンとは・・・?

2連コロンを使った宣言方法あります。「integer::c」と書きます。これまでに紹介した方法と、どこが違うのでしょうか。一つは、宣言に加えて初期値の設定ができるという点です。今までは初期値の設定は、宣言文で宣言した後、算術代入文やDATA文で値を入力する方法しかありませんでした。しかしこの宣言方法の場合、「integer::a=4」というように、宣言の段階で値の入力ができるのです。これにより、書く量が少なくなり、aの扱われ方が非常に分かりやすくなります。C言語では宣言+入力は当たり前のことなのですが、Fortran77の段階ではこれは不可能でした。もう一つの利点は、宣言を複数重ねることができるのです。例えば、後の章で学ぶ「配列」の宣言文である「DIMENSION文」がありまして、今までは「integer g」の後に、「dimension g(20)」と書いていました。2連コロンの宣言方法を使うと、これと型宣言文を一つにまとめて、「integer,dimension(20)::g」(宣言文同士の間にカンマが入ります)とすることができます。慣れるとこの宣言方法ばかり使用します。では、練習してみましょう。

Option説明

-O3
最適化を行います。最適化コンパイルは幾分長めの処理時間と、大きな関数に対する非常に多くのメモリを必要とします。これ以上の最適化オプションについては GCC ドキュメントを参照して下さい。特にループ展開は典型的な数値計算の Fortran プログラムにとっては調べる価値があるかも知れません。
-o file
出力先を file に指定します。
-g
オペレーティングシステムのネイティブのフォーマット (DBX, SDB,DWARF) でデバッグ情報を生成します。GDB はこのデバッグ情報に基づいて動作することができます。 DBX フォーマットを使用するほとんどのシステムにおいては、`-g' を指定すると、GDB だけが使用できる余分なデバッグ情報が使用可能になります。他の多くの Fortran コンパイラと異なり、GNU Fortran は `-g' を `-O' とともに使用することを許しています。最適化されたコードが通る近道は、時には驚くべき結果を生み出すかもしれません。定義したはずの変数が存在しなかったり、制御の流れが予想もしなかった場所に移動したり、結果が定数とわかる計算や、結果がすでに手元にある文は実行されなくなり、ある文がループの外に追い出されて別の場所で実行されたりします。それにも関わらず、このオプションは最適化された出力のデバッグを可能としています。これによって、バグを含むかもしれないプログラムに対してオプティマイザを使用することができるようになります。
-Wall
たとえマクロとの組み合わせであっても、避けたほうがいいと我々が推奨する用法や、簡単に避けることができると我々が信じている用法に関する場合に警告します



http://www-lab.imr.tohoku.ac.jp/~t-nissie/computer/c-tips/#section4
Fortran は write 文を 1 回実行する度に改行を行う。
(1)
do i=1,n
  write(6,*) a(i)
end do

(2)
write(6,*) (a(i),i=1,n)
では異なる結果となります。(1) では一つの要素毎に改行するのに対して、(2) では全ての要素を空白で繋げて出力したのち改行します。

Tips




その四「Fortran: WRITE文で改行させない方法」
FortranであるWRITE文で改行せず, その次のWRITE文で同じ行に書き続けたいときには書式 (FORMAT) の最後に$を付ける. こんなかんじ:

program suppress_new_line
 implicit none
 write(6,'(a,$)') 'abc'
 write(6,'(a)')   'def'
 write(6,'(a)')   'ghi'
end program suppress_new_line

その五「Fortran: 文字列をつなげる方法」
Fortranで二つの文字列をつなげるには//を使う. こんなかんじ:

     implicit none
     character*1 a,b,c,d
     character*1 e
     character*2 f
     a = 'X'
     b = 'Y'
     c = 'U'
     d = 'V'

     write(6,'(a)') a // b

     e = (c // d)
     f = (c // d)
     write(6,'(a)') e
     write(6,'(a)') f

     end


format文

format 文を使うと文番号を使う必要があります。以下のようにすると format 文を使わずに済ませることが出来ます。 write(6,'(a,f8.5,a,i3.3)') 'a = ',a,' i = ',i format 文を使わないことにより、プログラムの cut & paste のときに文番号の衝突を気にしなくて済みます。なお、上の例の i3.3 は 3 桁で出力し、それに満たない場合は 0 を補うと言う意味です

エラーメモ

  1. 1
main.f: In program `MAIN__':
main.f:9: 
        call init
        ^
End of source file before end of block started at (^)
make: *** [main.o] Error 1
utmcc000:~/programs/zalesak02/zal_source SaitouDaisuke$ 

これはmain.fにendを入れていなかったとき出たエラー・・・最低ですね


  1. 2
make: *** No rule to make target `cip.f', needed by `cip.o'.  Stop.

ファイル名をcip.fじゃなくてcpi.fにしてmakeしてたときに出たエラー・・・


  1. 3
Reference to intrinsic `EXP' at (^) invalid -- one or more arguments have incorrect type
init.f:26: 
            f(i,j) = (1/sqrt(2.0*3.141))*EXP(-(j**2)/2)*(1+0.01*cos(i/2))
                                                                ^
Reference to intrinsic `COS' at (^) invalid -- one or more arguments have incorrect type

(^)病人の本質的な'COS'の参照--1つ以上の議論には、不正確なタイプがあります。

型の宣言がおかしい、i,jはinteger(整数型)・・・当然cos()の中はflot


  1. 4
g77   -c  -O  init.f
init.f: In subroutine `init':
parameter.h:1: warning:
        common  f(0:im,0:jm),fs(0:im,0:jm), fss(0:im,0:jm),
        ^
Padding of 4 bytes required before `dt' in common block `_BLNK__' at (^) -- consider reordering members, largest-type-size first
     common /a/ f(0:im,0:jm),fs(0:im,0:jm), fss(0:im,0:jm),
    &       gx(0:im,0:jm), gsx(0:im,0:jm), gssx(0:im,0:jm),
    &       gv(0:im,0:jm), gsv(0:im,0:jm), gssv(0:im,0:jm),
    &       fn(0:im,0:jm), gnx(0:im,0:jm), gnv(0:im,0:jm),
    &       es(0:im), v(0:jm), x(0:im), istep, dt, dx, dy, sum

commonファイルにistepというのがあってIntegerだよ!


  1. 5
h153:~/programs/CV2_2DCIP/fortran se$ make
make: Circular main.f <- main.f dependency dropped.
make: Circular input.f <- input.f dependency dropped.
make: Circular output.f <- output.f dependency dropped.
make: Circular acip2d.f <- acip2d.f dependency dropped.
make: Circular poisson.f <- poisson.f dependency dropped.
g77 main.f input.f output.f acip2d.f poisson.f -o cv2
./cv2
make: *** [run_main] Segmentation fault

f(i,j)とかのi,jが設定値を超えると出るエラーらしい


  1. 6
h153:~/programs/CV2_2DCIP/fortran se$ make
make: Circular main.f <- main.f dependency dropped.
make: Circular input.f <- input.f dependency dropped.
make: Circular output.f <- output.f dependency dropped.
make: Circular acip2d.f <- acip2d.f dependency dropped.
make: Circular poisson.f <- poisson.f dependency dropped.
g77 main.f input.f output.f acip2d.f poisson.f -o cv2
./cv2
make:Permission to access file denied

Permissionつまり実行権限の問題!(chmod -777 で変えてやればいいときもあるが、なぜそのパーミッションになってしまっているかが問題)

____

COMMON分メモ

  便利なツールとして、次は「COMMON」文を紹介しましょう。ただ、余り使用は進められません。理由は、次章でお話しますが、COMMON文にはいろいろと問題が多いのです。これと同じ機能を持ち、かつ安全なモジュール(次章)がFortran90には用意されているので、あくまでFortran90 の人は、モジュールを使用するようにしてください。ここでのCOMMON文の紹介は、Fortran77人向けや一応の紹介だと受け止めてください。
 COMMON文というのは、宣言した変数を、大域要素と使用、というものです。大域要素というのは、局所要素の反対で、どのプログラム単位においても有効な要素のことです。これまでは、主プログラムで宣言した変数と同じ名前の変数を副プログラムで使用していたとしても、それらは見かけ上同じでもまったく別の変数でしたね。主プログラムで「x」を使い、副プログラムでも「x」を使ったとしても、それらはx1とx2のように別の変数でした。このCOMMON 文を使用すると、それらが同じ「x」になるのです。同じ変数なので、型宣言をする必要がなくなります。変数全てに対して型宣言を再びしなくてもよくなるというのは、仮引数が多ければ多いほどありがたみを感じます。
 使用方法は、まず主プログラムでCOMMON文を使用して、大域要素として変巣を定義します。「common /変数群の名前(領域名)/変数群」と書きます。領域名というのは、COMMON文を使用すると、「この領域にある変数が大域要素ですよ」ということを示す共通領域(ブロック)が作られます。その名前を、領域名のところに書きます。領域名は必須ではなく、書かなくても構いません。変数群を2種類以上に分け、それぞれに違った領域名をつける場合は、「common /p/a,b/q/c,d」のように、一つの領域名、変数群を書き、スラッシュを間に挟んで2つ目の領域名、、変数群を書いていきます。<br 2;  COMMON文を使用すると、主プログラム・副プログラムの実・仮引数が不要となります。また、先ほども申し上げたとおり、型宣言をしなくてすむようになります(配列宣言は必要ですが)。副プログラムにおいて、使用する変数を、「common /領域名/使用する変数」のように定義します。これで、何が引数になるのかを示すわけですね。COMMON文を使用した例が、下の例文です。

program main
 common /p/a(5),xmax,xmin
 integer::a,xmax,xmin
 print *,'最大値と最小値を求める。5個値を入力'
 read *,a(1:5)
 call sub
 print *,'最大値',xmax,'最小値',xmin
end program

subroutine sub
 common /p/a(5),xmax,xmin
 xmax=maxval(a,1)
 xmin=minval(a,1)
end subroutine sub


 この例では、「a(5),xmax,xmin」を変数群、「p」を領域名とする共通ブロックを作成しました。領域名は省略可能です。これにより、 SUBROUTINEを使用する際の引数と、副プログラム内での型宣言が省略されています。ちなみに、COMMON文において配列宣言がなされているので、主プログラムでの型宣言時に「integer::a(5)」という配列宣言は不要です。
 しかし、COMMON文は使いづらい面があります。それは、副プログラム内で使用する際には、COMMON文を書くときに、変数の並び順を買えずにそのまま書かなければならない、ということです。この場合ですと、「common /p/a(5),xmax,xmin」というのを、「common /p/xmax,a(5),xmin」としてはいけない、ということです。COMMON文で変数を共通利用する際のその方法というのは、変数の名前ではなく、変数の並び順なのです。副プログラムに書かれたCOMMON文の変数の並び順に、主プログラムでのCOMMON文の並び順が適用されてしまうのです。ですから、副プログラムで「common /p/xmax,a(5),xmin」と書いてしまうと、「xmax」は主プログラムの「a(5)」に、「a(5)」は主プログラムの「xmax」になってしまうのです。名前でなく順番で変数を対応させているので、「common /p/y(5),c,i」でも可能です。この、並び順を正確に書かなければならないというのが、COMMON文の使いづらさです。

 次は、「INTENT」と「OPTIONAL」です。共に仮引数に対して使います。INTENTから説明しましょう。INTENT文は、仮引数が主プログラムからの値や副プログラムで求めた値に対し、どう対応するのか、ということを決める文です。仮引数は、値に対する対応で3種類に分かれます。1つ目は、副プログラムでの計算をするために、主プログラムから必要な値を実引数から受け取ると言う、入力のみの引数です。2つ目は結果変数のことで、副プログラムで求めた値を、実引数に渡す、出力のみの引数です。3つ目は、今までは1回ぐらいしか出てこなかったと思いますが、入力も出力もこなす引数です(例えば、「a=a+2」のみが副プログラムでの計算であれば、、「a」は右辺で入力をし、左辺で計算結果を受け取って主プログラムに出力しています)。副プログラムで使用する仮引数が、この3つうちのどれなのかをコンピュータに対して明示的にするのが、INTENT文です。
 使い方はいたって簡単です。副プログラムでの変数の型宣言のときに、「intent(属性)」を書けばいいだけです。先ほどの3つのうち、1つ目は属性が「in」、2つ目は「out」、3つ目は「inout」となります。例えば、整数型で入力のみの仮引数nの宣言文は、「integer,intent (in)::n」と書きます。
 この文は使用しなくても、副プログラムが3つのうちどれなのかを自動的に判断してくれますので、今までの仮引数の宣言の仕方は間違ってはいません。しかし、コンピュータといえども、先ほどのEXTERNALでの関数の間違いと同じように、仮引数の属性を間違ってしまうことがあります。自分では1番目の属性としてプログラムを組んでいるのに、コンピュータが3番目だと判断してしまい、おかしな実行結果になることがあることは考えられます。ですから、そうしたエラーを出さないようにするためには、INTENT文を使用してその属性をはっきりさせることが大事だということです。また属性を宣言することにより、プログラマ自身にとっても、その変数の役割をしっかりと認識することができます。

 2つ目のOPTIONAL文は、対象の引数に省略可能を認める機能を持つ文です。副プログラムは実引数と仮引数の個数が一致するように書きますが、場合によっては省略できたほうが便利になることがあります。3つの引数を入力用として受け取る副プログラムがあるとしましょう。その副プログラムはこの3つを使って計算処理をするわけですが、2つに引数が減ったとしても、ほとんどの部分が3つの場合と同じ処理で可能だと分かりました。どうせほぼ同じ処理しかしないのであれば、わざわざ引数が2つ用の副プログラムをもう一個余分に作るよりも、3つ目の引数を省略可能な引数にして、引数が2つでも3つでも対応可能な副プログラムを作ったほうが簡単です。それを実現するのが、OPTIONAL文です。
 引数を省略可能にするには、型宣言文にOPTIONAL文を追加します。例えば、「real,optional::r」です。ただ、これだけでは不完全で、主プログラムにおいて、「INTERFACE」宣言をする必要があります。これは、INTERFACE文を使用して、「この副プログラムでは、この仮引数が使われていて、かつこの引数は省略可能なものとする」ということを宣言するのです。下の例をご覧ください。INTENTも一緒に使用しています。

program main
 implicit none
 interface
  real function f(x,y,z)
   real,intent(in),optional::z
   real,intent(in)::x,y
  end function f
 end interface

 integer::n
 real::x,y,z,kyori,f
 print *,'原点からある点までの距離を求める。ある点の次元数(2か3)を入力'
 read *,n
 if(n==2)then
  print *,'2次元で、ある点の座標を入力'
  read *,x,y
  kyori=f(x,y)
  print *,kyori
 else
  print *,'3次元で、ある点の座標を入力'
  read *,x,y,z
  kyori=f(x,y,z)
  print *,kyori
 endif
end program


real function f(x,y,z)
 real,optional::z
 real::x,y
 f=x**2+y**2
 if(present(z))f=f+z**2
 f=sqrt(f)
end function f


 INTERFACE宣言は、PROGRAM文やFUNCTION文のように、END分が付いて、ここで宣言を閉じます。INTERFACE宣言の中には、副プログラムの先頭と、変数の宣言文をそっくりそのまま書きます。書いたらEND文で副プログラムを閉じるのを忘れないようにしてください。これにより、「z」は省略可能な引数になります。ですから、副プログラムを呼ぶときに、実引数が「x」と「y」だけでも、また「z」を含めても、同じ副プログラムで対応することができます。
 副プログラム中にある「PRESENT」文は、省略可能な引数が今現在副プログラム中に存在しているかどうかを問い合わせる組み込み関数です。存在していれば真を返します。このプログラムの場合、「z」を引数として渡していれば、「f=f+z**2」、つまり「f=x**2+y**2+z**2」が行なわれることになります。

h153:~/programs/CV2_2DCIP/fortran saitoudaisuke$ make
make: Circular main.f <- main.f dependency dropped.
make: Circular input.f <- input.f dependency dropped.
make: Circular output.f <- output.f dependency dropped.
make: Circular acip2d.f <- acip2d.f dependency dropped.
make: Circular poisson.f <- poisson.f dependency dropped.
g77 main.f input.f output.f acip2d.f poisson.f -o cv2
/usr/bin/ld: Undefined symbols:
_MAIN__
collect2: ld returned 1 exit status
make: *** [cv2] Error 1
h153:~/programs/CV2_2DCIP/fortran saitoudaisuke$
判らんエラーだ???2010-02-03

タグ:

+ タグ編集
  • タグ:

このサイトはreCAPTCHAによって保護されており、Googleの プライバシーポリシー利用規約 が適用されます。

目安箱バナー