Fortran2003以降では、コマンドラインの引数を使ってファイルの読み込みが簡単にできます。
Fortranで数値計算をする際に、必要なインプットをファイルから与える、ことができるようになります。
#バージョン
gfortran: gcc version 8.3.0 (Homebrew GCC 8.3.0)
#読み込むファイル
今回は、インプットファイルの中で「=」を含む行の左がキーワード、右側が値、というインプットフォーマット
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は
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
となります。
使い方は、
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に、さらにインプットファイルを読み込むサブルーチンを追加します。
全体のコードは以下の通りです。
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
となります。ちゃんとファイルから値を読み込めています。