[Fortran] メモ

コンパイラはifort．
単語帳．毎回検索するのが面倒なので転載多め．元URLあり．
自前の検証メモも．

# 一般

### リンク集
http://jjoo.sakura.ne.jp/tips/fortlink/

### コーディングルール
http://www.mri-jma.go.jp/Project/mrinpd/eurostand.html

### 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

``` Fortran: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（コンパイル出来ない）．例：

``` Fortran: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文について

```Fortran
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することが出来る

```Fortran
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
```

https://www.sci.hokudai.ac.jp/grp/poc/top/old/software/other/fortran_tips/

### ループ・配列のインデックスで何もしない

`do i = n, n`でループすると`n`のみ実行，
`do i = n, n-1`でループすると何も実行しない：

```Fortran
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`

配列のインデックスの上限下限を越えた値を指定してもエラーは起きず，何もしないだけ．

```Fortran
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
```

## [ネームリスト](https://qiita.com/aisha/items/47cd2086d1316348b615)

## [文字列](https://qiita.com/aisha/private/5bccfad5f0ea8a53dfaa)











# 組み込み関数

### 論理型配列で.TRUE.の個数を数えるときは`count`

`sum()`は使えないが，代わりに`count()`がある．

```Fortran
logical :: a(3,4) = .FALSE.
a(1,2) = .TRUE.
a(2,4) = .TRUE.
write(*, *) count( a ) ! 2
```

`count`は他にも数字の配列`b`に対して`count( b > 0.0 )`等としても使える．

```Fortran
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](https://jp.xlsoft.com/documents/intel/cvf/vf-html/az/az08_48.htm)

### `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](https://www.unidata.ucar.edu/software/netcdf/docs-fortran/f90_The-NetCDF-Fortran-90-Interface-Guide.html)
[cockscomblog?: Fortran で NetCDF データを読み込む](https://cockscomb.hatenablog.com/entry/2013/02/11/151040)

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







# 高速化

[高速化プログラミング: トップ  >  メモリジャンプと高速化  > 構造体の配列]
(http://www.arcody.com/fast-programming/structure-array/)

- 配列の構造体：1つの配列がメモリ上で連続→1つの配列をループするときに有利
- 構造体の配列：異なる配列の要素がメモリ上で連続→1つの配列をループせず，いろいろな配列の情報を使うときに有利




# ファイル入出力

### ファイルの有無：access(path)==0で存在

``` Fortran
integer access ! これが無いとコンパイルエラーになる
character(len=256), parameter :: path = './test.bin'
status = access(path, ' ') ! 0でファイル存在
```

`access`の第2引数はモード．空白(' ')でファイルの有無を調べるが，'r'で読み取りアクセス権を調べる．

http://www.rivhiro-weather.com/knowledge/?p=140



### ファイルサイズは`inquire`

``` Fortran
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()`で

``` Fortran: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
```
https://sites.google.com/site/fcfortran/home/recipe/mkdir

























### ファイルのopen/close自体のコストは小さい

以下の通り
・ファイルを開いたまま読み込みを繰り返す
・ファイルを読む度にファイルをopen/closeする
を比較したが，どちらも30秒強と大差無し

``` Fortran
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は整数の左寄せ

``` Fortran
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 形反復を呼ぶらしい．

```Fortran
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)



### 変数の定義方法について

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

``` Fortran
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コンパイラ > コンパイル方法 > オプション](https://web.kudpc.kyoto-u.ac.jp/manual/ja/compilers/intel#option)

### メモリ不足 (relocation truncated to fit)の解消

ifortのコンパイルオプションに`-mcmodel=medium -shared-intel`を追加
他環境では異なる可能性もある

http://www.iimc.kyoto-u.ac.jp/ja/faq/comp/hpc/-02_2.html




### コンパイルオプションによる計算結果の差

`-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](https://www.phoronix.com/scan.php?page=news_item&px=GCC-Optimizations-E3V5-Levels)

日本語資料:
[大阪大学サイバーメディアセンター・日本電気株式会社: 並列コンピュータ 高速化技法の基礎]
(http://www.hpc.cmc.osaka-u.ac.jp/wp-content/uploads/2017/07/20170908.pdf)
[始める電子回路: コンパイラの最適化の罪](http://startelc.com/PIC/com_optimizing.html)

よほど気になったら以下の議論を確認．
[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 

https://www.gfd-dennou.org/library/davis/stpk/QandA/common_1-2.html









# プリプロセッサ

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

```Fortran: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 条件付きソースコード選択]
(https://www.nag-j.co.jp/nagfor/np52_manual/np52_manual_4_6.html)



### 複数条件はandが`&&`，orが`||`

### 複数の`#ifdef`を組み合わせる

`#ifdef`文で複数条件は不可．
`#if defined`を組み合わせる（これは3つ以上も可）．

```Fortran
#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)