Fortran90の基本文法まとめ

## はじめに
大気科学や海洋学や惑星科学などの地球物理学分野では、現代でもFortranが主流です。自分も研究で使用するため、いつでも確認できるように自分用のまとめ記事を書きました。とりあえず暗黙の型宣言を無効にしておきましょう。
```fortran:
program main
    implicit none
end program main
```
## 目次
1. 基礎文法
2. 型宣言
3. 制御構文
4. 入出力
5. サブプログラム
6. コンパイルと実行

注：これまで学んだ知識はFORTRAN77とFortran90/95の両方が混ざっているので、いくつかFortran90/95に当てはまらないものがあるかもしれません。

## 1. 基礎文法

- 添字は**1**スタート
- コメントアウトは`!`（77で使用されていた`C`も可）
- 文字列は`"Hello"`と`'Hello'`どちらでもよい
- 1行に書ける文字数は132文字
- コード中の改行は`&`
<details><summary>implicit noneについて</summary>

~~おまじない~~
iからnまでの英字を型名に指定すると、自動的に整数として扱われます。これは**暗黙の型宣言**と呼ばれます。思わぬエラーの原因にもなるので、`implicit none`で無効にし、自分で型宣言を行います。
</details>

例：ローレンツ・アトラクタの簡易式
```fortran:lorenz.f90
!ここはコメント
!Does the Flap of a Butterfly's Wings in Brazil Set Off a Tornado in Texas?
program lorenz
!おまじない
    implicit none

!型宣言
    real(8) :: x,y,z
    real(8) :: dx,dy,dz
    integer(4) :: i,n

!初期値の入力
    write(6,*) 'Plese enter x0, y0, z0'
    read(5,*) x, y, z

!試行回数の入力
    write(6,*) 'Plese enter Number'
    read(5,*) n

!n単位時間進んだ時の値
!dx/dt=-10x+10y, dy/dt=-xz+28x-y, dz/dt=xy-8x/3
    do i = 1,n
      dx = x - x*10 + y*10
      dy = y - x*z + x*28 - y
      dz = z + x*y - z*8/3
      x=dx
      y=dy
      z=dz
    end do
    print *, x, y, z

    stop
end program lorenz
```



## 2. 型宣言
Fortranは**最初**に変数・定数・配列等を全て指定する必要があります。
- 大文字と小文字は区別しない（`ans`と`ANS`は併用できない）
- 先頭の文字に数字は使用できない
- `-`と`.`は使用できない

### 変数・定数
デフォルトで`real`、`integer`は4バイトです。4バイトは単精度、8バイトは倍精度と呼ばれています。宣言した型から変更することもできますが、後から倍精度にする場合には精度が落ちます。
```fortran:
!型を宣言する
integer :: a,b         !整数
real    :: x           !実数
complex :: c           !複素数
logical :: ans         !論理(.true.と.false.)
double precision :: d  !倍精度実数(real(8)と同意)

character(len=10) :: s !長さ10の文字列（指定しない場合は1）

!初期値を指定する
integer :: a=5,b=-1
real    :: x=2.5
complex :: c=(1.3,0.5) !c=1.3+0.5i
logical :: ans=.true.

!データ領域の大きさを指定する
real(4) :: m           !4バイト（単精度）
real(8) :: n           !8バイト（倍精度）
!異なるコンパイラを用いるならkindパラメータが必要(本記事では省略)
real(kind=4) :: m

!定数の場合はparameterを追加する
integer(4),parameter :: days=365
```


### 配列
**``型名,dimension(要素数) :: 配列名``** もしくは **``型名 :: 配列名（要素数）``**
```fortran:
!配列を宣言する
!どちらの書き方も同じ
integer(4),dimension(5) :: A
integer(4) :: A(5)

!添字を[0 - 4]にする（デフォルトは1-5）
integer(4),dimension(0:4) :: A 
integer(4) :: A(0:4)

!配列の初期化
integer,dimension(5) :: A = (/1,3,5,7,9/)

!全ての要素を9にする 
A(:) = 9.0

!3×5の二次元配列
real(8),dimension(3,5) :: A
real(8) :: A(3,5)

!3×3×2の三次元配列
real(8),dimension(3,3,2) :: A
real(8) :: A(3,3,2)
```

#### 動的配列

大きさがnの配列を作成する場合、`real :: A(n)`と書くことはできない。
配列のサイズが不明である場合は、`allocatable`属性を付与してあげることで、配列用のメモリを確保することができます。配列を使用する際には`allocate`関数を用いてメモリを割り付けます。
使用後はメモリを解放してあげる必要があります。

```fortran:
real :: x,y
x=3*5
y=20

!動的配列の宣言
!どちらの書き方も同じ
real(8),allocatable,dimension(:) ::A 
real(8),allocatable :: A(:)
!二次元配列
real(8),allocatable,dimension(:,:) :: B

!メモリの割り当て
allocate(A(100))
!x×yの二次元配列
allocate(B(1:x,1:y))

!メモリの解放を忘れずに
deallocate(A)
deallocate(B)
```


### 四則演算・組み込み関数
```fortran:
abs(x)     !絶対値    
sqrt(x)    !平方根
mod(x,y)   !あまり
exp(x)     !指数
log(x)     !自然対数
max(a,b,c) !最大値
min(a,b,c) !最小値
sin(x)     !正弦
cos(x)     !余弦
tan(x)     !正接
asin(x)    !逆正弦
acos(x)    !逆余弦
atan(x)    !逆正接
!pi=4*atan(1.0)=3.1415...
```


###

## 3. 制御構文
### doループ
**`do 変数　= start,end,(刻み値)`**
**`end do`**
doループの変数は整数型(`integer`)でないとエラーになります。
この変数は別のループでも再利用できます。
ループを抜ける時は`exit`、ループの最初に戻るときは`cycle`を使います。
```fortran
integer(4) :: i,ans

do i = 1,5
   ans = ans+i
end do
print *, ans    !ans=15
```

### if文
`if (条件式) then`
`else if (条件式) then`
`else`
`end if`
```Fortran
integer(4) :: x

if(x==0) then
  print *, "Zero"
else if(mod(x,2)==0) then
  print *, "Even number"
else
  print *, "Odd number"
end if
```
**比較演算子**
|意味|演算子|意味|演算子|
|---|---|---|---|
|大きい|>|小さい|<|
|以上|>=|以下|<=|
|等しい|==|等しくない|/=|

**論理演算子**
|意味|演算子|
|---|---|
|否定|.not. x<y|
|かつ|x>0 .and. y>0|
|もしくは|x>0 .or. y>0|

### while
`do while(条件式)`
`end do`
```fortran:
!2の何乗か調べる
integer(4) a=512,n=0
do while(a<=1)
  a=a/2
  n=n+1
end do
```


## 4. 入出力
ファイルの読み書きの大まかな流れです。
1. openでファイルを開く
2. readでファイルを読む
3. 読み取ったデータを操作する
4. writeでデータを出力する

```fortran:
!hoge.txt={70,55,89,62,91}
program score
  implicit none
  integer :: i 
  integer,dimension(5) :: x  

  open(10,file='hoge.txt',action=read)

  do i = 1,5
      read (10,*) x(i)
      write (6,*) x(i)
  end do
  
  print *, 'all finishd!'
  close(10)
end program score
```


### open
```fortran:
open(unit=10, file='hoge.txt', action='read',status='replace')
!(処理)
close (10)
```
openしたら忘れずに`close 装置番号`しましょう。
|引数|説明 |デフォルト値
|---|---|---
|unit|装置番号。5が標準入力で6が標準出力。開いたファイルの便宜上の名前付けであり、複数のファイルを扱う際はそれぞれ別の数字にする必要がある。"unit="は省略可。|
|file|ファイル名|
|action|読み取り専用ならread、書き込み専用ならwrite、読み書きどちらもならreadwrite。|readwrite
|form|アスキー形式ならformatted、バイナリ形式ならunformatted。|formatted
|status|新規ファイルを作成するならnew。既存ファイルを使用するならold。同名ファイルが存在した場合上書きするならreplace。ファイルの存在の有無を問わないならunknownd。|unknownd
|recl|レコードの長さ|
|iostat|設定した変数に、ファイルが正常に開けた場合0を、そうでない場合には0以外を返す|



### read・write・print
`read(装置番号,書式) 値`と`write(装置番号,書式) 値`
アスタリスクは、前半部分が装置番号（unit）を、後半部分が書式（form）を指しています。printは画面へのみ出力可能です。

<details><summary>装置番号と書式について</summary>
装置番号：装置番号は標準入力が5、標準出力が6です。デフォルト値で画面への入出力になります。ファイルに入出力する場合、open文で指定した装置番号のファイルが使用されます。

書式：`( )`で囲って指定します。桁数を揃えて出力することができるため、例えばバイナリデータとして扱うことが出来るようになります。書式を空にしたままreadすると読み込まれません（行を読み飛ばすことが出来る）。
|形式|説明|例|
|---|---|---|
|I|整数で桁数を合わせる|I4
|F|少数で桁数を合わせる|F3.2

</details>

```fortran:
chalactar(20) :: name

!標準入力
read(5,*) name !pochi

!標準出力
write(6,*) "Hello ", name, "!"
!  Hello pochi!

!print文
print*, "Hello Fortran!"
!  Hello Fortran!
```
#### バイナリデータを扱う場合

バイナリ形式を扱う場合は、書式がないので指定しない（アスタリスク不要）。direct accessなら必ずreclも指定する。また、Fortranでは通常1数字4バイトなので、配列を倍精度にするとエラーになります。
```fortran:
!格子点が365×30のデータ（バイナリ形式）
real(4),dimension(365:30) :: data

open(11,file="hoge.bin", action='read', form='unformatted', &
    & access='direct',recl=365*30)

read(11)
```

## 5. サブプログラム
メインプログラムとサブプログラムの名前は別にする必要があります。メインプログラムに値を返す場合は関数、返さない場合はサブルーチンを用います。`intent(IN)`と追記すると入力専用の値として受け取られるため、変更を防ぐことが出来ます。

### サブルーチン
メインプログラムに値を返しません。`call(引数)`で呼び出すことが出来ます。
```fortran:sort.f90
!バブルソート
program sort
  implicit none
  integer(4) :: i,n
  integer(4) :: A(5)=(/3,1,4,2,5/)
  n=5
  call b_sort(n,A)
  stop
contains

!ここからサブルーチン
  subroutine b_sort(n,A)
    implicit none
    integer(4),intent(IN) :: n
    integer(4) :: A(n)
    integer(4) :: i,j,k

    do i = 1,n-1
      do j = i+1,n
        if(A(j)<=A(i)) then
          k=A(j)
          A(j)=A(i)
          A(i)=k
        end if
      end do
      print *, A(1),A(2),A(3),A(4),A(5)
    end do
    return
  end subroutine b_sort

end program sort
!---------------------------result---------------------------
!           1           3           4           2           5
!           1           2           4           3           5
!           1           2           3           4           5
!           1           2           3           4           5
```

### 関数
メインプログラムに値を1つ返します。
```fortran:gcd_lcm.f90
!最大公約数と最小公倍数を求める
program gcd_lcm
  implicit none

  integer :: a,b
  print *, "Enter number"
  read(5,*) a,b
  write(6,*) "GCD=",gcd(a,b)," LCM=",a*b/(gcd(a,b))

  stop
contains
!ここから関数
  integer function gcd(a,b)
    implicit none
    integer, intent(IN) :: a,b
    integer :: aa,bb,tmp
    aa=a
    bb=b
    do
      tmp=mod(aa,bb)
      if(tmp==0) exit
      aa=bb
      bb=tmp
    end do
    gcd = bb
    return
  end function gcd

end program gcd_lcm
```

## 6. コンパイルと実行
gfortranでコンパイルしています。成功すればa.outという名前の実行ファイルが作成されます。
```bash:
gfortran hoge.f90
./a.out

#実行ファイルの名前を指定してコンパイル
gfortran hoge.f90 -o hogehoge
./hogehoge
```

## おわりに
Fortranとはまだまだ長い付き合いになりそうなので、もっと勉強してもっと仲良くなれればいいなと思っています。

## 参考サイト
[Fortran演習 (地球惑星物理学演習), 東京大学, 2022](https://amanotk.github.io/fortran-resume-public/index.html)

[Fortran入門, nag](https://www.nag-j.co.jp/fortran/index.html)

[Fortranでバイナリを読み書きするときのあれこれ](https://akebi28.hatenablog.jp/entry/2017/02/28/081307)