コンパイラはifort.
単語帳.毎回検索するのが面倒なので転載多め.元URLあり.
自前の検証メモも.
一般
リンク集
コーディングルール
Fortranのバージョンについて
ifortは拡張子が.f90
でもFortran95/2003/2008で追加された機能を含めてコンパイルしてくれる.
→「~~はFortran90より新しい機能だから実装できないな」ということはない.
オブジェクト指向等もどんどん使っていける.
※ただしgfortranは.f03
等と拡張子を明示する必要があるので注意
https://t.doujin-constant.net/post/58493424257/fortran2003%E6%8B%A1%E5%BC%B5%E5%AD%90%E5%95%8F%E9%A1%8C
[森川(2008)惑星大気大循環モデルの設計と開発]
(http://www.gfd-dennou.org/arch/prepri/2008/kobe-u/081028_epasemi_morikawa/pub/kobe-seminar-dcpam-ver1.1.pdf)
- 力学過程と物理過程の区別はソースコードとしてはイマイチ
- OOPは結局読みづらい場合も
- gtool5の設計思想
- RDocによるドキュメント自動生成
言語
手続き(subroutine, function)
optional属性はそれを引数にして呼び出したサブルーチンでも引き継がれる
総称名を経由してもOK
module mymod
implicit none
interface myfunc
module procedure myfunc1
end interface myfunc
contains
subroutine myfunc1(a)
integer, optional, intent(in) :: a
if (present(a)) then
write(*, *) 'yes'
else
write(*, *) 'no'
endif
end subroutine myfunc1
subroutine myfunc2(a)
integer, optional, intent(in) :: a
call myfunc1(a)
end subroutine myfunc2
end module mymod
program main
use mymod, only : myfunc1, myfunc2, myfunc
implicit none
call myfunc1(1) ! yes
call myfunc1() ! no
call myfunc2(1) ! yes
call myfunc2() ! no
call myfunc (1) ! yes
call myfunc () ! no
end program main
勿論総称名に,区別できない手続きを含めることはNG(コンパイル出来ない).例:
interface myfunc
module procedure myfunc1
module procedure myfunc3
end interface myfunc
subroutine myfunc3(a, b)
real, optional, intent(in) :: a, b
!real, intent(in) :: b
if (present(a)) then
write(*, *) 'yes'
else
write(*, *) 'no'
endif
end subroutine myfunc3
このmyfunc3
の引数からoptional属性を外せば(コメントアウト部)OK.
optional
属性の付いた引数のif文について
program main
implicit none
call mysub( 1 ) ! ok
call mysub( 0 ) ! ng
call mysub() ! ng,これもエラーにならない
contains
subroutine mysub( a )
integer, optional, intent(in) :: a
! write(*, *) present(a)
! write(*, *) a == 1
! write(*, *) a
if ( present( a ) .and. a == 1 ) then
write(*, *) 'ok'
else
write(*, *) 'ng'
endif
end subroutine mysub
end program main
musub
内のif文について,これでもコンパイル・実行可能.
引数aを与えていないときには"a==1"の判定はできないはずだが,期待通りに動作する.
しかし,"a==1"単独では使用不可(コメントアウトした2行目でsegmentation fault).
pure, elemental
[Qiita@sage-git: FortranのPureなFunction]
(https://qiita.com/sage-git/items/65339c9074472497e8ab)
[ドウジンテイスウ.log: Fortranのelemental関数での並列化と副作用]
(https://t.doujin-constant.net/post/143318921814/fortran%E3%81%AEelemental%E9%96%A2%E6%95%B0%E3%81%A7%E3%81%AE%E4%B8%A6%E5%88%97%E5%8C%96%E3%81%A8%E5%89%AF%E4%BD%9C%E7%94%A8)
配列を返す関数
result()
を使わない文法.未検証.
[stackoverflow: Function Returning an array in Fortran]
(https://stackoverflow.com/questions/3828094/function-returning-an-array-in-fortran)
ループ
do構造名
do構造名を使うことで,入れ子のdoループから一気にexitすることが出来る
integer :: i, j
loop1: do i = 1, 2
loop2: do j = 3, 7
write(*, *) i, j
if (j == 6) exit loop1
enddo loop2
enddo loop1
! 1 3
! 1 4
! 1 5
! 1 6
ループ・配列のインデックスで何もしない
do i = n, n
でループするとn
のみ実行,
do i = n, n-1
でループすると何も実行しない:
write(*, *) 'loop1'
do i = 1, 1 ! i = 1のみ実行
write(*, *) i
enddo
write(*, *) 'loop2'
do i = 1, 0 ! 何もしない
write(*, *) i
enddo
i = n -> i = n-1
として実行したい場合はdo i = n, n-1, -1
配列のインデックスの上限下限を越えた値を指定してもエラーは起きず,何もしないだけ.
integer :: a(3,4)
a(:,:) = 1
write(*, *) sum( a(: 1,:) ) ! 4
write(*, *) sum( a(: 0,:) ) ! 0
write(*, *) sum( a(:-1,:) ) ! 0
write(*, *) sum( a( 4:,:) ) ! 0
write(*, *) sum( a( 5:,:) ) ! 0
a(:0,:) = 0 ! 何にも代入されない
write(*, *) sum( a ) ! 12
a(:-1,:) = 0
write(*, *) sum( a ) ! 12
a(4:,:) = 0
write(*, *) sum( a ) ! 12
a(5:,:) = 0
write(*, *) sum( a ) ! 12
ネームリスト
文字列
組み込み関数
論理型配列で.TRUE.の個数を数えるときはcount
sum()
は使えないが,代わりにcount()
がある.
logical :: a(3,4) = .FALSE.
a(1,2) = .TRUE.
a(2,4) = .TRUE.
write(*, *) count( a ) ! 2
count
は他にも数字の配列b
に対してcount( b > 0.0 )
等としても使える.
real :: b(3,4) = 0.0
b(1,2) = 1.0
b(2,4) = 1.0
write(*, *) count( b > 0.0 )
sqrt
はintegerには未対応
http://www.nag-j.co.jp/fortran/FI_10.html#GenericVsSpecificNames
を見てみると,sqrt
は単精度実数・倍精度実数・複素数の総称名!
nint
:最も近い整数の取得
huge
: その型でとりうる最大値
integer :: a
=> huge(a)
でa
の最大値.
負の最大値は単純に-huge(a)
でOK.
[stackoverflow: Fortran: the largest and the smallest integer]
(https://stackoverflow.com/questions/9569756/fortran-the-largest-and-the-smallest-integer)
モジュール
[Qiita@aisha: [Fortran] モジュール メモ]
(https://qiita.com/aisha/items/03b7b4c582b317c2ffdb)
ライブラリ
NetCDFの読み書き
The NetCDF Fortran 90 Interface Guide
cockscomblog?: Fortran で NetCDF データを読み込む
PythonのDateTime
, TimeDelta
等を実装
https://github.com/wavebitscientific/datetime-fortran
高速化
[高速化プログラミング: トップ > メモリジャンプと高速化 > 構造体の配列]
(http://www.arcody.com/fast-programming/structure-array/)
- 配列の構造体:1つの配列がメモリ上で連続→1つの配列をループするときに有利
- 構造体の配列:異なる配列の要素がメモリ上で連続→1つの配列をループせず,いろいろな配列の情報を使うときに有利
ファイル入出力
ファイルの有無:access(path)==0で存在
integer access ! これが無いとコンパイルエラーになる
character(len=256), parameter :: path = './test.bin'
status = access(path, ' ') ! 0でファイル存在
access
の第2引数はモード.空白(' ')でファイルの有無を調べるが,'r'で読み取りアクセス権を調べる.
ファイルサイズはinquire
INQUIRE(FILE=filename, SIZE=file_size)
単位はバイト.
ファイルが存在しない場合は-1.
https://stackoverflow.com/questions/17711835/what-is-a-good-way-to-get-file-size-in-bytes-using-fortran
https://www.ibm.com/support/knowledgecenter/ja/SSGH4D_14.1.0/com.ibm.xlf141.aix.doc/language_ref/inquire.html
ディレクトリの作成はcall system()
で
module mymod
implicit none
contains
subroutine mkdir(outdir)
character(len=*), intent(in) :: outdir
character(len=256) command
write(command, *) 'if [ ! -d ', trim(outdir), ' ]; then mkdir -p ', trim(outdir), '; fi'
write(*, *) trim(command)
call system(command)
end subroutine
end module mymod
program main
use mymod
implicit none
call mkdir('./test/sub')
call mkdir('./test/sub')
end program main
ファイルのopen/close自体のコストは小さい
以下の通り
・ファイルを開いたまま読み込みを繰り返す
・ファイルを読む度にファイルをopen/closeする
を比較したが,どちらも30秒強と大差無し
program main
implicit none
character(len=256), parameter :: path = './test.bin'
real, allocatable :: data(:,:)
integer, parameter :: nx = 1440, ny = 720, nz = 366, nitr = 100, unit = 100
integer iz, iitr
integer t1, t2, t_rate, t_max, diff
call system_clock(t1)
do iitr = 1, nitr
open(unit, file=trim(path), status='old', form='unformatted', access='direct', recl=4*nx*ny)
do iz = 1, nz
read(unit, rec=iz) data
enddo
close(unit)
enddo
call system_clock(t2, t_rate, t_max)
write(*, *) 'test1:', clock2time(t1, t2, t_rate, t_max)
call system_clock(t1)
do iitr = 1, nitr
do iz = 1, nz
open(unit, file=trim(path), status='old', form='unformatted', access='direct', recl=4*nx*ny)
read(unit, rec=iz) data
close(unit)
enddo
enddo
call system_clock(t2, t_rate, t_max)
write(*, *) 'test2:', clock2time(t1, t2, t_rate, t_max)
contains
function clock2time(t1, t2, t_rate, t_max) result(time)
double precision time
integer t1, t2, t_rate, t_max
if ( t2 < t1 ) then
diff = (t_max - t1) + t2 + 1
else
diff = t2 - t1
endif
time = diff / dble(t_rate)
end function clock2time
end program main
バイナリの読み書き
[羊小屋の落書き: Fortranでバイナリを読み書きするときのあれこれ]
(http://akebi28.hatenablog.jp/entry/2017/02/28/081307)
PythonでFortranのバイナリ出力を読む
[Qiita@convection: pythonでFortran出力を読む]
(https://qiita.com/convection/items/f3be09bb280df030f980)
その他
コマンドライン引数の取得
引数の個数:command_argument_count()
引数の長さ:call get_command_argument(i, length = length, status = status)
引数の中身:call get_command_argument(i, arg, status = status)
で取得.正常な動作ならstatus = 0
http://www.nag-j.co.jp/fortran/tips/tips_GetCommandArgument.html
書式
https://www.rs.kagu.tus.ac.jp/yama/f90/FORMAT.html
https://qiita.com/chirimen/items/f867855f8707ed4e104c
http://www.nag-j.co.jp/fortran/FI_14.html
https://amanotk.github.io/fortran-resume-public/chap06.html
i0は整数の左寄せ
x = 10
write(*, '(i0)') x
万能クラスclass
https://qiita.com/cure_honey/items/e6ca856788ee6c3e42bb
http://fortran66.hatenablog.com/entry/2013/03/21/022046
https://t.doujin-constant.net/post/84930925809/fortran%E3%81%A7quick-sortunlimited-polymorphic
Pythonのリスト内包表記(的なもの)
(/ /)
または[ ]
を配列構成子と呼び,以下のような表記を配列構成 DO 形反復を呼ぶらしい.
integer :: a(5) = (/ (i,i=1,5) /) ! 1, 2, 3, 4, 5 となる
real :: b(6) = (/ (i*5,i=1,6) /) ! 5, 10, 15, 20, 25, 30 となる
[nag: Fortran 入門: 配列 12.3 配列構成子と reshape 組込み関数]
(https://www.nag-j.co.jp/fortran/FI_12.html)
変数の定義方法について
「現在」推奨されるのは(以下は倍精度実数の例)
use,intrinsic :: iso_fortran_env, only: real64
real(real64) a ! real(kind=real64)でも同じだがkind=は省略可
だが,個人開発ではreal(8)
で十分とのこと.double precision
よりも統一性がとれる.
ただし簡易表記ではコンパイラ間やコンパイルオプションで精度を変更できてしまう(real
やdouble precision
も精度変更可能).
https://qiita.com/implicit_none/items/ab0632357bab0f549cf2
コンパイル
コンパイルオプション
京都大学学術情報メディアセンター: Intelコンパイラ > コンパイル方法 > オプション
メモリ不足 (relocation truncated to fit)の解消
ifortのコンパイルオプションに-mcmodel=medium -shared-intel
を追加
他環境では異なる可能性もある
コンパイルオプションによる計算結果の差
-O0
から-O3
の違いはここ.
案外これらを変えても実行時間は変わらない場合も(要実験)
[インテル® C++ および Fortran コンパイラー 18.0 最適化クイック・リファレンス・ガイド 【pdf直リンク】]
(https://jp.xlsoft.com/documents/intel/compiler/18/Quick-Reference-Guide-Intel-Compilers-v18.pdf)
コンパイルオプションによるベンチマークの差:
phoronix: GCC Optimization Level Benchmarks With A 4GHz Intel Skylake CPU On Ubuntu 64-bit
日本語資料:
[大阪大学サイバーメディアセンター・日本電気株式会社: 並列コンピュータ 高速化技法の基礎]
(http://www.hpc.cmc.osaka-u.ac.jp/wp-content/uploads/2017/07/20170908.pdf)
始める電子回路: コンパイラの最適化の罪
よほど気になったら以下の議論を確認.
[stackoverflow: OpenMP compiles with different flags gives different results]
(https://stackoverflow.com/questions/23030478/openmp-compiles-with-different-flags-gives-different-results)
[Intel community: Different results with -O0 -openmp or -O3 -openmp]
(https://community.intel.com/t5/Intel-Fortran-Compiler/Different-results-with-O0-openmp-or-O3-openmp/td-p/979219)
[OpenMP Forum: OpenMP and floating-point operations]
(https://forum.openmp.org//viewtopic.php?f=3&t=1516)
また他言語になるが,「gcc 最適化 バグ」で検索すると-O1~3
の信頼性が低いことが分かる
-qopenmp
による変化
[stackoverflow: Calling an internal subroutine inside OpenMP region]
(https://stackoverflow.com/questions/25628034/calling-an-internal-subroutine-inside-openmp-region)
OpenMP
ifort -qopt_report
プリプロセッサ
#if .TRUE.
や#if .FALSE.
が使える
program main
implicit none
#if .TRUE.
write(*, *) 'true'
#else
write(*, *) 'false'
#endif
end program main
というプログラムを$ ifort -fpp main.f90
でコンパイル・実行すると,true
と表示される.
#if .TRUE.
を#if .FALSE.
に変えるとfalse
と表示されるようになる.
#if 1
は#if .TRUE.
と,#if 0
は#if .FALSE.
と同じ意味.
[NAG Fortran コンパイラ 5.2 マニュアル: 4.6.5 条件付きソースコード選択]
(https://www.nag-j.co.jp/nagfor/np52_manual/np52_manual_4_6.html)
複数条件はandが&&
,orが||
複数の#ifdef
を組み合わせる
#ifdef
文で複数条件は不可.
#if defined
を組み合わせる(これは3つ以上も可).
#if defined(A) && defined(B)
#if(defined A && defined B)
[stackoverflow: Boolean in ifdef: is “#ifdef A && B” the same as “#if defined(A) && defined(B)”?]
(https://stackoverflow.com/questions/1312132/boolean-in-ifdef-is-ifdef-a-b-the-same-as-if-defineda-definedb)
[stackoverflow: How to add a 'or' condition in #ifdef]
(https://stackoverflow.com/questions/2998864/how-to-add-a-or-condition-in-ifdef/2998876)