ifort/gfortranで動くコードをnvfortranへの移行するときにトラブったこと

自前の第一原理電子状態計算パッケージ[ecalj](https://github.com/tkotani/ecalj)はgfortran,ifortで動きます。

これをnvfortran24.1で実行可能にしようとして、3,4日かかりました。ちょっと心が折れそうになりましたが助けてもらったりで何とかなりました。今日のgfortran,ifortはものすごくバグが減り信頼性が高いんですが、nvfortran24.1はそうは言えないです。修正点はgitのコミット間差分 git diff 875831 bb9f09で見れますが（まだgithubへはアップロードできてない）、要点を書いときます。


* インストールと設定が面倒。ぼくは.bashrcに
```bash
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を入れて引き渡すと内部で不定値（０だったり大きな数だったり）に化ける。DEBUGモードだと通るので-O2のバグだと思うが、どのソースまでかは調べきれてない。仕方ないので数値を固定して渡して通常モードでは動くようにした。なので（あまりよろしくないけど）有効相互作用を離れた原子間も計算するwmat_allのオプションが効かなくなっている。いずれはMLO基底に移行するつもりなのでそうしてある。ifort/gfortranでは復活させても動く。

* シングルコアで走らせるときもmpirun -np 1が必要だった
* まずデバッグモード(コンパイルオプション-C -g -traceback -O0)で通したあと,-O2で通そうとしたが、300程度のソースファイルのうち-O2できなかったものが以下のCMakeList.txtのように2ファイルあった
```CMakeLists.txt
...
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で展開し直してやらないといかんかった。おかしな値を出して平然としている（エラーが出るわけでない）。
```fortran
-      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
```
以下もダメ。ムダな論理を持ち込みたくないけどしかたない。
```fortran
-      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) 
+        
```
```fortran
-  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   
```

```fortran
-            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 
```

```fortran
-        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を使うことが多いので自前で書いた。

* 以下のような埋め込みはダメだった。あきらめて数値列を入れた。
```fortran
integer,parameter:: lla(1:(lmaxx+1)**2) = [((l,m=1,2*l+1),l=0,lmaxx)] !nvfortran24.1 ```
```

* actionは気に入らないらしい。
```fortran
-    open(newunit=ifvcoud, file=trim(vcoudfile), action='read',form='unformatted')
+    open(newunit=ifvcoud, file=trim(vcoudfile),form='unformatted')
```

* reshapeもダメな場合があった。
```fortran
- 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もこれだとコケる。
```fortran
-          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のみ
```fortran
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

```