LoginSignup
4
0

More than 1 year has passed since last update.

[Fortran] メモ

Last updated at Posted at 2019-05-09

コンパイラは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)惑星大気大循環モデルの設計と開発

  • 力学過程と物理過程の区別はソースコードとしてはイマイチ
  • OOPは結局読みづらい場合も
  • gtool5の設計思想
  • RDocによるドキュメント自動生成

言語

手続き(subroutine, function)

optional属性はそれを引数にして呼び出したサブルーチンでも引き継がれる

総称名を経由してもOK

optional.f90
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(コンパイル出来ない).例:

not_compiled.f90
    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
ドウジンテイスウ.log: Fortranのelemental関数での並列化と副作用

配列を返す関数

result()を使わない文法.未検証.
stackoverflow: 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:最も近い整数の取得

NINT

huge: その型でとりうる最大値

integer :: a => huge(a)aの最大値.
負の最大値は単純に-huge(a)でOK.

stackoverflow: Fortran: the largest and the smallest integer

モジュール

Qiita@aisha: [Fortran] モジュール メモ

ライブラリ

NetCDFの読み書き
The NetCDF Fortran 90 Interface Guide
cockscomblog?: Fortran で NetCDF データを読み込む

PythonのDateTime, TimeDelta等を実装
https://github.com/wavebitscientific/datetime-fortran

高速化

高速化プログラミング: トップ > メモリジャンプと高速化 > 構造体の配列

  • 配列の構造体: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()

mkdir.f90
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でバイナリを読み書きするときのあれこれ

PythonでFortranのバイナリ出力を読む

Qiita@convection: pythonでFortran出力を読む

その他

コマンドライン引数の取得

引数の個数: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 組込み関数

変数の定義方法について

「現在」推奨されるのは(以下は倍精度実数の例)

use,intrinsic :: iso_fortran_env, only: real64
real(real64) a ! real(kind=real64)でも同じだがkind=は省略可

だが,個人開発ではreal(8)で十分とのこと.double precisionよりも統一性がとれる.
ただし簡易表記ではコンパイラ間やコンパイルオプションで精度を変更できてしまう(realdouble 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直リンク】
コンパイルオプションによるベンチマークの差:
phoronix: GCC Optimization Level Benchmarks With A 4GHz Intel Skylake CPU On Ubuntu 64-bit

日本語資料:
大阪大学サイバーメディアセンター・日本電気株式会社: 並列コンピュータ 高速化技法の基礎
始める電子回路: コンパイラの最適化の罪

よほど気になったら以下の議論を確認.
stackoverflow: OpenMP compiles with different flags gives different results
Intel community: Different results with -O0 -openmp or -O3 -openmp
OpenMP Forum: OpenMP and floating-point operations
また他言語になるが,「gcc 最適化 バグ」で検索すると-O1~3の信頼性が低いことが分かる

-qopenmpによる変化
stackoverflow: Calling an internal subroutine inside OpenMP region

OpenMP

ifort -qopt_report

プリプロセッサ

#if .TRUE.#if .FALSE.が使える

main.f90
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 条件付きソースコード選択

複数条件は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)”?
stackoverflow: How to add a 'or' condition in #ifdef

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0