Fortranの数値計算でインプットファイルをファイルから読み込む

Fortran2003以降では、コマンドラインの引数を使ってファイルの読み込みが簡単にできます。
Fortranで数値計算をする際に、必要なインプットをファイルから与える、ことができるようになります。


#バージョン
gfortran: gcc version 8.3.0 (Homebrew GCC 8.3.0) 

#読み込むファイル
今回は、インプットファイルの中で「=」を含む行の左がキーワード、右側が値、というインプットフォーマット

```stan.in
model = "Hubbard"
method = "CG"
lattice = "square"
W= 2
L = 2
t = 1
U = 8
nelec = 4
2Sz = 0
```
をFortranで読み込んでみます。

#コマンドラインの引数を読み込む
まず、コマンドラインの引数を読み込みます。つまり、

```
a.out AAA BBB
```
となっているときに、AAAとBBBを文字列として読み込みます。
（参考：http://www.nag-j.co.jp/fortran/tips/tips_GetCommandArgument.html）

それを実現するmoduleは

```fortran
module readfiles
    implicit none

    interface readarg
        module procedure readarg_1
        module procedure readarg_2
    end interface readarg

    contains 

    subroutine readarg_1(arg1)
        implicit none
        integer::i,length,status
        character(:), allocatable,intent(out) :: arg1
        intrinsic :: command_argument_count, get_command_argument
        if (command_argument_count() .ne. 1) then
            write(*,*) "error! num. of arguments should be 1"
            stop
        end if
        i = 1
        call get_command_argument(i, length = length, status = status)
        if (status == 0) then
            allocate (character(length) :: arg1)
            call get_command_argument(i, arg1, status = status)
            write(*,*) arg1
        end if

    end subroutine

    subroutine readarg_2(arg1,arg2)
        implicit none
        integer::i,length,status
        character(:), allocatable,intent(out) :: arg1,arg2
        intrinsic :: command_argument_count, get_command_argument
        if (command_argument_count() .ne. 2) then
            write(*,*) "error! num. of arguments should be 1"
            stop
        end if
        i = 1
        call get_command_argument(i, length = length, status = status)
        if (status == 0) then
            allocate (character(length) :: arg1)
            call get_command_argument(i, arg1, status = status)
            write(*,*) arg1
        end if

        i = 2
        call get_command_argument(i, length = length, status = status)
        if (status == 0) then
            allocate (character(length) :: arg2)
            call get_command_argument(i, arg2, status = status)
            write(*,*) arg2
        end if        

    end subroutine    


end module readfiles
```
となります。

使い方は、

```fortran
program main
    use readfiles
    implicit none
    character(:), allocatable::arg1
    character(:), allocatable::arg2
    call readarg(arg1)
end program
```
です。readargというサブルーチンでコマンドラインの引数を読み込むことができます。
ここでは、readarg(arg1)とreadarg(arg1,arg2)という、一つの場合と二つの場合に対応してみました。
interface文を使ったことで、同じサブルーチンで読み込むことができます。

#インプットファイルを読み込む
上のmoduleに、さらにインプットファイルを読み込むサブルーチンを追加します。
全体のコードは以下の通りです。

```fortran
module readfiles
    implicit none

    interface readarg
        module procedure readarg_1
        module procedure readarg_2
    end interface readarg

    contains 

    subroutine readarg_1(arg1)
        implicit none
        integer::i,length,status
        character(:), allocatable,intent(out) :: arg1
        intrinsic :: command_argument_count, get_command_argument
        if (command_argument_count() .ne. 1) then
            write(*,*) "error! num. of arguments should be 1"
            stop
        end if
        i = 1
        call get_command_argument(i, length = length, status = status)
        if (status == 0) then
            allocate (character(length) :: arg1)
            call get_command_argument(i, arg1, status = status)
            write(*,*) arg1
        end if

    end subroutine

    subroutine readarg_2(arg1,arg2)
        implicit none
        integer::i,length,status
        character(:), allocatable,intent(out) :: arg1,arg2
        intrinsic :: command_argument_count, get_command_argument
        if (command_argument_count() .ne. 2) then
            write(*,*) "error! num. of arguments should be 1"
            stop
        end if
        i = 1
        call get_command_argument(i, length = length, status = status)
        if (status == 0) then
            allocate (character(length) :: arg1)
            call get_command_argument(i, arg1, status = status)
            write(*,*) arg1
        end if

        i = 2
        call get_command_argument(i, length = length, status = status)
        if (status == 0) then
            allocate (character(length) :: arg2)
            call get_command_argument(i, arg2, status = status)
            write(*,*) arg2
        end if        

    end subroutine    

    subroutine readfromfiles(filename)
        implicit none
        character(len=*),intent(in)::filename
        integer::io
        integer,parameter :: max_line_len = 4000
        character(max_line_len) linebuf
        integer::equalposition
        integer::ivalue,length
        real(8)::dvalue
        character(:), allocatable::cvalue,ckeyword
        

        open(101,file=filename)
        do 
            read(101,'(a)',iostat = io) linebuf
            if (io < 0) exit
            write(*,*) "Original string: ",trim(linebuf)
            equalposition = index(trim(linebuf),"=")
            if (equalposition.ne. 0) then
                length = len(trim(linebuf(:equalposition-1)))
                allocate(character(length) :: ckeyword)
                !write(*,*) "keyword ",ckeyword

                length = len(trim(linebuf(equalposition+1:)))
                allocate(character(length) :: cvalue)
                !write(*,*) "value ",cvalue
                write(*,*) "We read from data: "
                if (ckeyword == "model") then
                    write(*,*) ckeyword, cvalue
                else if (ckeyword == "method") then
                    write(*,*) ckeyword, cvalue
                else if (ckeyword == "lattice") then
                    write(*,*) ckeyword, cvalue
                else if (ckeyword == "W") then
                    read(cvalue,*) ivalue
                    write(*,*) ckeyword, ivalue
                else if (ckeyword == "L") then
                    read(cvalue,*) ivalue
                    write(*,*) ckeyword, ivalue
                else if (ckeyword == "t") then
                    read(cvalue,*) dvalue
                    write(*,*) ckeyword, dvalue
                else if (ckeyword == "U") then
                    read(cvalue,*) dvalue
                    write(*,*) ckeyword, dvalue
                else if (ckeyword == "nelec") then
                    read(cvalue,*) ivalue
                    write(*,*) ckeyword, ivalue
                else if (ckeyword == "2Sz") then
                    read(cvalue,*) dvalue
                    write(*,*) ckeyword, dvalue
                else
                    write(*,*) "unsupported input key!"
                    stop
                end if    

                deallocate(cvalue)
                deallocate(ckeyword)

            end if
            
        end do

        close(101)

        return
    end subroutine


end module readfiles

program main
    use readfiles
    implicit none
    character(:), allocatable::arg1
    character(:), allocatable::arg2
    call readarg(arg1)
    call readfromfiles(arg1)
end program



```

```read(101,'(a)',iostat = io) linebuf```の```(a)```は、文字列として1行丸々読み込みようにするものです。
```iostat = io```は正常に読み込めたかどうかを判定するioを返します。ですので、
```if (io < 0) exit```によって、ファイルの末尾に達した場合にはdoループを抜けることができます。
そのあと、空白部分を除去するtrimを使って```trim(linebuf)```としています。
次に、```=```の場所を調べるため、```index(trim(linebuf),"=")```を使っています。この```index```は文字列を左からサーチして特定の文字がある場所を返す関数です。もし文字列に```=```がなければ0を返します。
```trim(linebuf(:equalposition-1))```は```=```が入っている時に、その左がわの文字列のことです。文字列は```a(1:3)```のようにすると一部分を取り出すことができます。これらによって、```=```の左がわをキーワード、右側を値、とすることができました。
```allocate(character(length) :: ckeyword)```は多分Fortran2003以降で使えるもので、allocateを使って文字列の長さを指定しています。
あとは、読み込んだキーワードにしたがって処理をIf文で分岐させるだけです。このコードでは、整数で返ってくるものは文字列から整数を作り、実数で返ってくるものは文字列から倍精度実数を作るようにしています。
```read(i,*) a```は文字列iをaの型（例えば整数や実数）に変換することができます。
上のコードを実行すると、

```
stan.in
 Original string: model = "Hubbard"
 We read from data: 
 model "Hubbard"
 Original string: method = "CG"
 We read from data: 
 method "CG"
 Original string: lattice = "square"
 We read from data: 
 lattice "square"
 Original string: W= 2
 We read from data: 
 W           2
 Original string: L = 2
 We read from data: 
 L           2
 Original string: t = 1
 We read from data: 
 t   1.0000000000000000     
 Original string: U = 8
 We read from data: 
 U   8.0000000000000000     
 Original string: nelec = 4
 We read from data: 
 nelec           4
 Original string: 2Sz = 0
 We read from data: 
 2Sz   0.0000000000000000     
```
となります。ちゃんとファイルから値を読み込めています。