自前の第一原理電子状態計算パッケージecaljはgfortran,ifortで動きます。
これをnvfortran24.1で実行可能にしようとして、3,4日かかりました。ちょっと心が折れそうになりましたが助けてもらったりで何とかなりました。今日のgfortran,ifortはものすごくバグが減り信頼性が高いんですが、nvfortran24.1はそうは言えないです。修正点はgitのコミット間差分 git diff 875831 bb9f09で見れますが(まだgithubへはアップロードできてない)、要点を書いときます。
- インストールと設定が面倒。ぼくは.bashrcに
NVARCH=`uname -s`_`uname -m`; export NVARCH
NVCOMPILERS=/opt/nvidia/hpc_sdk; export NVCOMPILERS
MANPATH=$MANPATH:$NVCOMPILERS/$NVARCH/24.1/compilers/man; export MANPATH
PATH=$NVCOMPILERS/$NVARCH/24.1/compilers/bin:$PATH; export PATH
export PATH=$NVCOMPILERS/$NVARCH/24.1/comm_libs/mpi/bin:$PATH
export MANPATH=$MANPATH:$NVCOMPILERS/$NVARCH/24.1/comm_libs/mpi/man
export OPAL_PREFIX=$NVCOMPILERS/$NVARCH/24.1/comm_libs/openmpi/openmpi-3.1.5
export OMPI_MCA_coll=^hcoll
と書いてます。
https://docs.nvidia.com/hpc-sdk//hpc-sdk-install-guide/index.html を見て24.1を使うように設定したんです。https://docs.nvidia.com/hpc-sdk//compilers/hpc-compilers-user-guide/index.html#mpi-use をみてMPI設定もおこなったけど、どうもぼくがよくわかってないのでmpifortがgfortranとかちあってしまう。あきらめていったんgfortranを消したら動くようになった。math library
は、最後のCMakeFiles.txtにあるようにmklをリンクしている。
-
どうにも理解不能な挙動が一箇所あった( call wmatqk_MPIで引数にnrws2に1を入れて引き渡すと内部で不定値(0だったり大きな数だったり)に化ける。DEBUGモードだと通るので-O2のバグだと思うが、どのソースまでかは調べきれてない。仕方ないので数値を固定して渡して通常モードでは動くようにした。なので(あまりよろしくないけど)有効相互作用を離れた原子間も計算するwmat_allのオプションが効かなくなっている。いずれはMLO基底に移行するつもりなのでそうしてある。ifort/gfortranでは復活させても動く。
-
シングルコアで走らせるときもmpirun -np 1が必要だった
-
まずデバッグモード(コンパイルオプション-C -g -traceback -O0)で通したあと,-O2で通そうとしたが、300程度のソースファイルのうち-O2できなかったものが以下のCMakeList.txtのように2ファイルあった
...
elseif(FC MATCHES "nvfortran" AND DEFINED DEBUG)
message(STATUS "====== nvfortran debug =======================")
foreach(target IN ITEMS ${SOURCES})
set_source_files_properties(${target} PROPERTIES COMPILE_FLAGS "-C -g -traceback -O2") #why -O2 needed here???
endforeach()
elseif(FC MATCHES "nvfortran")
message(STATUS "====== nvfortran release =======================")
foreach(target IN ITEMS ${SOURCES})
if(${target} MATCHES "../subroutines/pwmat.f90"
OR ${target} MATCHES "../subroutines/rseq.f90")
set_source_files_properties(${target} PROPERTIES COMPILE_FLAGS "-C -g -traceback -O0")
else()
set_source_files_properties(${target} PROPERTIES COMPILE_FLAGS "-O2")
endif()
endforeach()
...
endif()
- どうも配列構成子がネストしたロジックに弱いようだ。二重のインデックスが入る場合とか関数呼び出しに関わる場合とか。以下のようにdoで展開し直してやらないといかんかった。おかしな値を出して平然としている(エラーが出るわけでない)。
- nlmto = sum([ (sum([((2*l+1)*nindxv(l+1,iclass(ic)),l=0,nl-1)]),ic=1,natom) ])
+ nlmto=0
+ do ic=1,natom
+ nlmto=nlmto+sum([((2*l+1)*nindxv(l+1,iclass(ic)),l=0,nl-1)])
+ enddo
以下もダメ。ムダな論理を持ち込みたくないけどしかたない。
- nlnx = maxval([( sum( nindx(1:nl,ic)), ic=1,nclass)])
+ do ic=1,nclass
+ nnn1(ic)=sum(nindx(1:nl,ic))
+ enddo
+ nlnx = maxval(nnn1)
+
- blks =[(sum( [ (2*ltab(io)+1, io=iorb,ntab(iorb)) ] ),iorb=1,norb)]
+ do iorb=1,norb
+ blks(iorb) =sum( [ (2*ltab(io)+1, io=iorb,ntab(iorb)) ])
+ enddo
- wtff = [(1d0,it=ns1,nctot), (wfacx(-1d99, ef, ekc(it), esmr),it=max(nctot+1,ns1),ns2)]
+ wtff(ns1:nctot) =1d0
+ do it=max(nctot+1,ns1),ns2
+ wtff(it) = wfacx(-1d99, ef, ekc(it), esmr) !bugfix 2023-5-18 ns1==>max(nctot+1,ns1)
+ enddo
- ndimx=maxval([ (sum([(sum((2*li+1)*idxdn(li+1,:,j)), li=0,n0-1)]), j=1,nspec) ])
+ ndimx=0
+ do j=1,nspec
+ nnnx=0
+ do li=0,n0-1
+ nnnx= nnnx + sum((2*li+1)*idxdn(li+1,:,j))
+ enddo
+ if(nnnx>ndimx) ndimx=nnnx
+ enddo
-
findlocも整数以外は動かないようだ。論理配列でのfindlocを使うことが多いので自前で書いた。
-
以下のような埋め込みはダメだった。あきらめて数値列を入れた。
integer,parameter:: lla(1:(lmaxx+1)**2) = [((l,m=1,2*l+1),l=0,lmaxx)] !nvfortran24.1 ```
- actionは気に入らないらしい。
- open(newunit=ifvcoud, file=trim(vcoudfile), action='read',form='unformatted')
+ open(newunit=ifvcoud, file=trim(vcoudfile),form='unformatted')
- reshapeもダメな場合があった。
- reshape(ppb(nc1:ncnv,nc1:ncnv,:,icp),shape=[nv,nv,mdim],order=[2,1,3])
-
optional変数で、presentを認識しない場合があった。
-
論理チェック. if(A.and.B)でA=.false.のときにBが存在してないとコケる。
-
forallもこれだとコケる。
- forall(ir=1:nr) v0pot(ib)%v(ir,:)= sum([(v0pot(ib)%v(ir,isp),isp=1,nsp)])/nsp
+ do ir =1,nr
+ v0pot(ib)%v(ir,:)= sum([(v0pot(ib)%v(ir,isp),isp=1,nsp)])/nsp
+ enddo
まとめ:とにかくGPU化を睨んで、nvfortran24.1に通した。修正すればそれなりにきちんと答えを出すようになったが、エラーもはかずに平然とおかしな答えを出すので、このコンパイラで論理的な部分の開発は困難かな、という印象。エラー時のtracebackが弱いようにも感じた。ただ、直すときちんと動くようにはなった。mklがリンクできた。findloc,配列構成子,optional,reshapeなどを使ってなければ、そこまで困難はなかったかも知れない。
DEBUGモードで配列のレンジチェックなども通したのでその点は良かった。すこしバギーなところもとれた(昔のfortranの仕様で,real(8)::M(10,11,12)をM(15,3,4)と呼び出しても通りました。これをやっちゃってたところを消した)。
VASPやQuestaalなどGPU化したなどというバージョンはどうしても一発バージョン(メンテしない)というものになってしまっているようだ。nvfortranがいまいちなところとも関係あるかも知れない。GPU化は時代の流れだと思うが一社独占では限界がある。AMDなどにも頑張って欲しいところだ。
雑ですけどfindlocも置いときます。汎用ではないです。countも作りました。use m_nvfortranとしてオーバーライドします。dimは見てないです。dim=1のみ
module m_nvfortran !this is because nvfortran24.1 have no implementation to findloc
public findloc,count
interface findloc
module procedure findlocl,findloci
endinterface findloc
interface count
module procedure countl
endinterface count
private
contains
pure integer function findlocl(lll,value,dim,back) !this is for
implicit none
intent(in):: lll,value,dim,back
integer:: nnn,ns,ne,inc,i
logical:: lll(:),value
logical,optional:: back
integer,optional:: dim
nnn=size(lll)
if(present(back)) then
if(back) then
ns=nnn
ne=1
inc=-1
endif
else
ns=1
ne=nnn
inc=1
endif ! nn1 = findloc([(rsmh1(i,j)>0d0,i=1,nnx)],value=.true.,dim=1,back=.true.)! 1st MTO sets
findlocl=0
do i=ns,ne,inc
if(intl(value)-intl(lll(i))==0) then
findlocl=i
return
endif
enddo
contains
pure integer function intl(lll)
logical,intent(in):: lll
if(lll) intl=1
if(.not.lll)intl=0
endfunction intl
end function findlocl
pure integer function findloci(lll,value,dim,back)
implicit none
intent(in):: lll,value,dim,back
integer:: nnn,ns,ne,inc,i, lll(:),value
logical,optional:: back
integer,optional:: dim
nnn=size(lll)
if(present(back)) then
if(back) then
ns=nnn
ne=1
inc=-1
endif
else
ns=1
ne=nnn
inc=1
endif
findloci=0
do i=ns,ne,inc
if(lll(i)==value) then
findloci=i
return
endif
enddo
end function findloci
integer function countl(lll)
integer:: i,m
logical:: lll(:)
m=0
do i=1,size(lll)
if(lll(i)) m=m+1
enddo
countl=m
end function countl
endmodule m_nvfortran