Fortran備忘録(1) Fortran 90/95の超基本メモ


プログラミング苦手な上に、Fortran太古の言語とか言われてるけど、研究で必要なので備忘録。。。
これだけ覚えれば、あとは実際のコード見ながらでもなんとか読めるでしょうという程度まで。
簡単な順に書いているわけではないので、初心者は2周読めば理解できるかも。
コーディングする際は別記事[Fortran備忘録(2) 用途別Fortran90/95基本メモ]()参照。

## 環境構築とコンパイル
Macなのでコンパイラのgfortranが内包されたgccをHomebrew環境下でインストール

```shell
brew install gcc #インストール
which gfortran   #gfortranのパス確認
gfortran -v      #バージョン確認
```

```hello.f90
program hello
	print *, 'Hello, world!'
end program hello
```

```shell
gfortran hello.f90 -o hello.out
./hello.out
```

テキストファイルへの出力リダイレクトを行う場合、

```shell
gfortran hello.f90 -o hello.out
./hello.out > output.txt
```

```
Hello, world!
```

モジュールなどの副プログラムを使用しているコードを実行する場合、

```shell
gfortran mod_params.F90 mod_one.F90 mod_two.F90 main.F90 -o main.out
```

とするか、あるいは、以下のようにオブジェクトファイルを作る。

```shell
gfortran -c mod_params.F90
gfortran -c mod_one.F90
gfortran -c mod_two.F90
gfortran -c main.F90
gfortran mod_one.o mod_two.o main.o -o main.out
```

※よく使うgfortranのデバッグオプション

|オプション|意味|
|:-----:|:-----:|
|-c|オブジェクトファイルの生成|
|-O2|（`main.F90`の直後に置いて）最適化|
|-fbounds-check|配列の領域外からの参照を検出|


## ルール

- Fortran90/95であることを明らかにするため、```.f```より```.f90```を使うのが慣例。(自由形式のFortran90/95コードでも```.f90```がよく使われるっぽい(?)プリプロセッサを使う際は`.F90`と大文字にする！（[用途別Fortran90/95基本メモ]()参照）)
- 必ず**宣言部→実行部→副プログラム部**の順で書く。（実行部の中で宣言できない。）
- プログラムは```end```で終える。
- program文は省略可能。
- コメントアウトは```!```（行頭、行末）、またはFortran77のみ(?)行頭に`c`。コメント内日本語ok。
- 大文字小文字の区別が無いので、```SUBROUTINE```とか```CONTAINS```とか、区切りを大文字にしておくと見やすい。
- プログラム名と同じ変数名は良くない。
- f95の自由形式に対応しているコンパイラがほとんどなので一行の文字数問題は気にしなくていい。(132文字まで)規定文字数を超えると書いてあっても無視される。
- 一行を分ける時は一行目の最後に```&```。（文字列中で改行もでき、その時は一行目末に加え、二行目の最初にもスペース入れずに```&```を入れる。）
- **```implicit none```を必ず宣言する。**外すと暗黙の型規約が適用され、i~nが整数、他が実数になるらしい。

## 制御構造（と簡単なサンプルコード）
### 反復のdoループ

```fortran
	do i = 1, 100
		sum = sum + i
	enddo
```

<details><summary>全体</summary><div>

--------------------------
```do_loop.f90
PROGRAM do_loop
	implicit none
	integer :: i, sum
	sum = 0

	do i = 1, 100				!
		sum = sum + i			!
	enddo						!

	write(*, *) 'sum = ', sum
END PROGRAM do_loop
```
--------------------------
</div></details>

後判定の```do while```構文もある。

### 条件分岐のif

```fortran
    if (i == 0) then
        write(*,*) i, "は0です。"
    else if (i < 0) then
        write(*,*) i, "は負です。"
    else if (mod(i,2) == 0) then
        write(*,*) i, "は偶数です。"
    else
        write(*,*) i, "は奇数です。"
    end if
```

<details><summary>全体</summary><div>

--------------------------
```if_oddeven.F90
PROGRAM if_oddeven
    implicit none

    integer :: i
    print *, "整数を入力："
    read(*,*) i

    if (i == 0) then
        write(*,*) i, "は0です。"
    else if (i < 0) then
        write(*,*) i, "は負です。"
    else if (mod(i,2) == 0) then
        write(*,*) i, "は偶数です。"
    else
        write(*,*) i, "は奇数です。"
    end if

END PROGRAM if_oddeven
```
--------------------------
</div></details>

`then`必須。
ケース分岐の```select```構文もある。

**※比較演算子の別表記**
どっちも使える。

|意味|よくあるやつ|ドットと文字|
|:-----:|:-----:|:-----:|
|Equal|`x == y`|`x .eq. y`|
|Not Equal|`x /= y`|`x .ne. y`|
|Greater Than|`x > y`|`x .gt. y`|
|Greater than or Equal|`x >= y`|'x .ge. y`|
|Less Than|`x < y`|`x .lt. y`|
|Less than or Equal|`x <= y`|`x .le. y`|



not equalは`!=`じゃなくて、`/=`なことに注意！

**論理演算子**
[否定] `.not.`を使う。
[かつ、または] Fortranに`&&`や`||`はない！`.and.`や`.or.`を使う。
[論理値の比較]`.eqv.``.neqv`もある。

##宣言と型
## 型
- 数値型
    - 整数型
        - 基本整数型`integer`
    - 実数型`real(8)`
    - 複素数型
- 文字型
- 論理型`logical`
    - 論理定数`.true.`あるいは`.false.`を持つ。（が、表示される時は`T`と`F`。）

## 宣言

```fortran
implicit none

real(8) :: real_variable  
real(8), parameter :: pi  = 3.14159265359d0
real(8), allocatable :: real_number_data(:)

```

`::`はあるに越したことはないっぽい。
`allocatable`

`parameter`

```fortran
allocate(real_number_data(number_of_data))
```

save属性の局所変数は複数回の副プログラム呼び出しに対して値を保持する。
(saveが付いていなくてもFortranでは初期化（宣言と代入が同一文で行われる）時に自動的にsave属性が付く)（厄介）


```fortran
SUBROUTINE glob
	integer, save :: global_variable = 5
END SUBROUTINE glob
```

## 入出力
### 標準入出力（ディスプレイへの入出力）

```fortran
!入力
read(*,*) x, y, z
!出力
print *, 'sum =', sum, 'dayo!'
write(*,*) 'sum =', sum, 'dayo!'
```

入力は```read```を使えば、ターミナルで変数の入力を求められるようになる。（```print *, 'x=?'```などとしておく。）
出力は普通```print```か```write```を用いる。
```read(*,*)```や```write(*,*)```の```*```は出力先、書式を決める装置番号や編集記述子(printの```*```も)というものだが、どちらも標準として```(*,*)```。
**```print```は後ろにカンマがいる。**
出力するものは、```sum```などの変数でも```'sum = '```などの文字列でもok。

### ファイル入出力と開閉






##関数、サブルーチン、モジュールの違いと使い方
**関数は返り値がある。**
**サブルーチンは返り値がない。**（他言語でいう`void`型関数。）
**モジュールはサブルーチンや関数をまとめたもの。**

### 関数(function)
後述するサブルーチンとは違って、使用時`CALL`する必要はない。

**仮引数には以下のintent属性を付けることが必須。**（ローカル変数には不要。）

|属性|実引数からの値の受け取り|値の演算・代入と保持|
|:----:|:----:|:----:|
|`intent(in)`|○|×|
|`intent(out)`|×|○|
|`intent(inout)`|○|○|

要は、関数・サブルーチンを使うときに引数に入れる値が決まっている場合は`intent(in)`、
関数・サブルーチン中で仮引数の値が代入される場合は`intent(out)`。

### サブルーチン(subroutine)
個人的に「手続」っていうイメージ。
他言語でいう`void`型関数。
使用時は`CALL name_of_subroutine(arg)`で呼び出す。
主プログラム内で`CALL`の行に辿り着くと、副プログラムの一行目から最後まで演算が行われ、主プログラムの次の行に移る。副プログラムの演算を途中で抜け出したい時は`return`。
冒頭でサブルーチンには返り値がないと言っているが、`intent(out)`を用いて値を計算・代入することは可能であり、実際二つ以上の値を返したい場合はサブルーチンが用いられる。


### モジュール(module)
使う時は```USE name_of_module```で呼び出す。
`USE`宣言は`implicit none`より前。
モジュール内のサブルーチンを呼び出す時は、`USE`宣言した後に`CALL name_of_subroutine(arg)`。
Fortranでグローバル変数を用いる際は`save`などもあるが、一般に`mod_params.F90`などのモジュールを作り、その中に宣言するのが通例。


## プログラム単位と副プログラムについて
ファイル分割が許される最小単位

- 主プログラム（program 〜 end program ）
- 外部副プログラム（containされない関数、サブルーチン）
- モジュール
- block data

の4種類。以下で一つずつ説明。
**副プログラムとは、関数あるいはサブルーチン**のこと。
以下のように、副プログラムは**内部副プログラム、外部副プログラム、モジュール副プログラム**の３種類。

### 主プログラム(main program)
内部副プログラムは主プログラム内に含まれる(containされる)関数、サブルーチン。主プログラム内でしか使えないから、機能拡張に向かない。外部副プログラムでいい。

```fortran
PROGRAM name_of_program
	!宣言部
	!実行部
CONTAINS
	!内部副プログラム(internal subprogram)1
	!内部副プログラム(internal subprogram)2
	!...
END PROGRAM name_of_program
```

### 外部副プログラム(external subprogram)
反対に、主プログラム内に含まれない(containされない)関数、サブルーチン。

```fortran
FUNCTION name_of_function(arg)
	!宣言部
	!実行部
CONTAINS
	!内部副プログラム
END FUNCTION name_of_function
```

```fortran
SUBROUTINE name_of_subroutine(arg)
	!宣言部
	!実行部
CONTAINS
	!内部副プログラム
END SUBROUTINE name_of_subroutine
```


### モジュール(module)
```fortran
MODULE NAME_OF_MODULE
	!...
CONTAINS
	!モジュール副プログラム（この中にさらに内部副プログラムを入れることは可能。）
END MODULE NAME_OF_MODULE
```

### block data
宣言部だけでファイルを作るという今はほぼ使われない化石。（コンセプトは好きだけど。。共通ブロックに問題があって良くない(?)）
今はパラメータを集めたモジュールを作ればいい。
[nag Fortran 入門: 知識として必要な過去の Fortran](https://www.nag-j.co.jp/fortran/FI_17.html#ImplicitTyping)が詳しい。
（Fortranは古くから存在するプログラミング言語の中では比較的数式を簡単に表現できるのもあって、アカデミックな、特に地球科学分野などで伝統的に使われていることが多く、時にはFORTRAN77のような読みづらいものが残っていることもある。このページは他にもそのような色んな過去の産物（GO TOとか文番号とか）のことが書いてあって、Fortran95からより古いバージョンを追っていくにあたり非常に助かった。）

<br>

※このように、基本色んなプログラム単位の中に内部副プログラムを置くことができるが、**内部副の中に内部副とする事だけは不可（内部副プログラムは入れ子構造禁止）**。

## 参考
[nag Fortran入門](https://www.nag-j.co.jp/fortran/index.html)
恐らくインターネット上ではFortran入門として一番見られているサイト。ありがとうありがとう

[Fortran 入門: 知識として必要な過去の Fortran](https://www.nag-j.co.jp/fortran/FI_17.html#ImplicitTyping)
色んな過去の産物のことが書いてあって、Fortran95からより古いバージョンを追っていくにあたって非常に助かった。

[数値計算のためのFortran90/95プログラミング入門](https://www.amazon.co.jp/数値計算のためのFortran90-95プログラミング入門-第2版-牛島-省/dp/462784722X/ref=sr_1_1?__mk_ja_JP=カタカナ&dchild=1&keywords=数値計算のためのFortran90%2F95プログラミング入門&qid=1595273696&sr=8-1)
有名な書籍(amazon)。最近(2020)第2版が出たみたいなので。。

[Fortran演習](https://amanotk.github.io/fortran-resume-public/)
とてもわかりやすい。
東京大学理学部地球惑星物理学科の地球惑星物理学演習の授業で作られたみたい。さすが東大

## 個人的メモ
覚えながら、忘れそうなところを、合間時間を使って書いていく。
コードを読んでてわからなかったところを書く。