LoginSignup
5
4

More than 1 year has passed since last update.

fortran tips と自己流書き方ガイド

Last updated at Posted at 2023-02-13

今のfortranは良くなった。コンパイラバグが激変したし、aptでmklをインストールできblas,lapack,fftwなどがおよそ標準装備となった。そもそも数学関数が高速になりいちいちblasを呼ばなくていいようになったし、ベクトル表記や関数などがだいぶと整備されてきた。もちろん気の利かない文法もないこともないが古いコードが動くのも良い
(python活用しているがpython3にはひどい目にあった。いまだにsystem関係がメタメタな気がする。subprocessにはなんでargを切って入れるー意味わからん.os.systemでよい)。

良いコードを書くための自己流ガイドライン

(ときどき書き直しています)。

  • 理想はコメント行の不要なコード あるいは論文の数式との対応だけが書いてあるだけで理解できるようなもの。コメントを読まないとわからないようなプログラムは問題がある。暗号のような符牒を工夫しないほうがいい---計算アルゴリズムの本質と無関係な論理構造は極力持ち込まない。「便利な関数化」をしたつもりが保守性の悪い「ブラックボックス\inブラックボックス\inブラックボックス...」にならないように。
  • 情報の二重記述をしないようにする. たとえば本体と呼び出し側での整合性をプログラマに要求するような書き方は極力避けたい。「二重記述がダブルチェックになる」というようなことはほとんどない。極力interface文は書かないー自動決定されるような部分を人間が書くべきではない。moduleを活用するのはインターフェイス文を自動で作ってくれるという意味においても有効。たとえばpure関数,optional変数やsize関数の有効化、名前結合、などをさせるには、関数やサブルーチンをmoduleに入れてやりuseで用いる必要がある。
  • module変数は(ごく少数で明示的な例外を除いては)protectすること。デフォルトをprivateとしpublicを書く。useするときは必ずuse,onlyで明示する。
  • 変数の居場所(どのmoduleに属するか?)を決めること。データの唯一性を守ること。常にその変数の属するmoduleをuseして読み出す。たとえば原子数を指す変数は唯一でないといけない。そのコピーを作ってはならない。データの入った行列の本体aがmodule Aに、そのサイズを指定する変数nがmodule Bに入っているのは全く問題がない。問題なのはそのサイズ変数nをmodule Aとmodule Bに重複して入れてしまうこと。また、たとえばある場所で、突然電子密度なり、原子基底のインデックスなりが必要になったりする、このときモジュールであれば、use onlyのみで済む。サイドエフェクトなく引用できる。変数を増やすなどのコードの改変も本質的に無関係なところをいじる必要がない。「データを読むのはどこからでも自由に読める(onlyで明記できる.変なデータ経路を作らずに直接参照で書くべし!)、生成するのは一箇所でのみ」を守る。ポータビリティなど気にしなくて良いーいろんな関数・道具の有用性は文脈の中でのみそれが実証されているんだから。
  • 要するにモジュールを唯一性クラスオブジェクトとして利用することになる。複数作れないので不便だと思うかもしれないけれどルールが単純になるご利益もある;唯一性があるので名付ける必要がなくselfがいらないし、唯一性のため混乱を生じずどこからでもダイレクトに読める。具体的には
    • まず「ファイル読み込みはモジュールでおこなって(allocateもおこなって)そのデータはprotectしておいて他から読み出すときは必ずuse onlyする」とするのがよい。
    • 初期化はbootstrap方式で連鎖的にモジュール初期化ルーチン(引数ほとんどなし)を呼んで、複数のモジュールに初期化データを貯めていく。これらの変数は計算が終了するまで変更されないワンスライトなデータセットである。
    • サブルーチンはモジュール内に書く。サブルーチンが必要とする入力データはuse onlyでどこかのモジュールから読みだす。自分自身で言えば、昔のサブルーチンbndfpは引数が50個とかあったんだけど、指示引数3個ぐらいに削減しました。
    • モジュール名はm_foobarとする。
    • 計算の流れをコントロールする指示的な変数のみサブルーチン入力引数で渡す。
    • 出力値はモジュール変数で戻す(サブルーチン内でallocateする)のを基本とする。サブルーチン引数を出力値としない。ただし、そうするとuseする変数が出力値となるから「出力値が関数コールから離れたところに書かれている」ということがおきる。ただcyclic searchのできるエディタであれば対応できる。メインルーチンにおいてサブルーチンAのコールでモジュール内変数aが変更されそれを次のサブルーチンB内で使うということも発生しうる点、すなわちデータ生成の手順が見えにくくなりうる点があり注意は必要ー明瞭になるようにしておく必要がある。
    • 要するにサブルーチンの仕事は「複数の必要なモジュール変数を読み取って、なにか計算してそのサブルーチンを含むモジュールのモジュール変数をallocateしてデータを書き出す」という仕事となる。そのデータは唯一性を持つものとして扱いそのデータが必要なときにはダイレクトにそれを読むこと。サブルーチンの引数で渡さない。
    • 小さなサブルーチン(基本的にI/Oの明瞭な小さい関数や数学関数など)は独立関数とする。あるいはモジュールに格納しておくー*.modというインターフェイスを自動生成してくれるので最低限の型チェックをコンパイラに任せることができる。モジュール変数はなくて良い。最新のgfortranだと-fallow-argument-mismatch
      をつけないと古いコードは動かなかったりするーこれを考えてもモジュールに格納する方式にしていくのがよい。たとえばmodule m_utilというモジュールを作ってcontainsで多数のサブルーチンを格納する。

ぐらいに注意すればなんとかなる。

  • そもそもfortranの限度をわきまえるべき。fortranで書けるのはfile to fileのfeedfowardなプログラム(ほとんどすべての変数はワンスライトなもの)ぐらいに心得ておく。グルーランゲージで外側から小さなfortranプログラムを連鎖的に実行させる。iterationしたいなら外側で回す。現状、自身の電子状態計算のコードではそうなっていないが将来的には(もうすこしfortran+pythonが進化すれば)それが良いと思っている。
  • block文はとても有効。単なるラベルをつけてのブロック区切りとしても使える。延々と長いコメントに似たラベルでも良い。ヘタにサブルーチン化するまえにブロック化すればよい。if文なども含めてラベルをしっかり書く。局所変数やuseを使えるしallocateをかなり減らすことができる。emacsなど自動でendblockにラベル補完してもらえるエディターを使う。
  • インプット変数の多いサブルーチンはmodule化する。ほとんどのインプット変数は「環境変数」的なもので、初期化時点でどこかのモジュールに属させれば良いものであったりする。
  • moduleに入れたサブルーチンをuseして使う場合にはコンパイラが変数の型整合性をチェックしてくれる。なのでサブルーチンは(モジュール変数がない場合でも)モジュールに入れてuseして使ったほうがいい。並列コンパイルmake -jでやればまあまあ速度は何とかなる。
  • logical,integer,real(8),complex(8),character(バイト数)で書く。kindなどと書かない。kindってfortranの方言すぎる。そんな概念を知っても脳が疲れるだけで何の得もない。real(foobar)のfoobarはバイト数。これとkindは無関係だしreal(real64)もアホみたい(real64=8と代数化する意味はない)。正直kindという概念を持ち込んだア太郎先生を問い詰めたいという気もする。
  • 文字列操作はそれなりにできる。が、無理がたたりそうなら,call system(foobar)でpythonでも呼ぶ。ファイルtoファイルでやり取りする。何千行かあった文字列操作部分をcall system(foobar.py)に直したら100行程度になった(と同時に(問題にならない部分ではあったが)pythonの遅さを再認識した)。
  • いまだにopen(newunit=ifi...)を紹介していないfortran学習文献もある。マニュアルを見たほうがよいことも多い。新機能は実装できてないこともあり得るし実装されててもコケることもあるのでgfortranとifortの二種のコンパイラは通す(スタンダード実装であるがfortranの正式な取り決めにない拡張もありえる)。正直、英語圏と日本語圏では情報量に差があるなと感じることも多いー(できたら避けたいけれども)英語検索も必要。調べているとひょっとすると「日本語だと大多数の教育用文献でnewunitを紹介していない。」のかもしれないー初心者はマニュアル見ないだろうし不安になってきました。
  • サブルーチン化、モジュール化、クラス化に本当に意味があるか?本質的に論理的なクリーンさを与えることができているのか?を考える。脳の無駄遣いになるような新規概念の導入はやめよう。特に構造体=>クラス化は柔軟性に乏しく危険な気がする。
  • module dependencyのチェッカー(Makeのための依存リスト)はどうやる?検討したが現状は自前pythonコードで行っている。別記事。
  • どうしても古いコードとの共存になる(先達のコードが古い場合)。とにかくモジュールに入れていくのがいいかもしれない。
  • 必ずエンバグするので、テストシステムを工夫しておかないといけない。テストシステムとgitを使わないと開発が維持できない。昼飯とかのスキマ時間にこまめに起動する。
  • コンパイラバグがないわけではない。-O0で通るならコンパイラバグの可能性が高い。-O0でもあかんのならたぶんコンパイラバグではなかろう。どのソースを-O0にするかを見つけて、conditional compileの指示をmakefileに書き込まないといけない。
  • バグ取りにはオプション(-gとか-tracebackとか)をつけて、問題ありそうなコードをコンパイルし直して実行する。デバッガは面倒だし役に立たない(というかぼくが使えてないだけか)。数値的なエラーだとチェックライトをいれるしかないし、基本的にはバグを追い詰めていくような方法しかない。バグは複数ありえることも多い。ホームズにでもなった気分で追い詰める。
  • 混合プログラミング。どうしようもない場合以外、いたずらな複合プログラミングは避ける。へたにbind(C)などとするのは面倒になりうる。call systemでファイルtoファイルのデータIOでやれるならそれがよいかもしれない。pythonを親にすえるのなら、ごく限定的なインプット引数のみ(あるいは引数なし)のfortran関数のコールがいいかもしれない。独立したプログラムを連鎖的にコールしていくのと似てはいるが、モジュール内部の変数を保った状態で、連鎖的にコールしていけるアドバンテージはある。
  • 整数化するには、nint,ceiling,floorを使い分ける。負数に対してintは定義が面倒。
  • 以下にいくらか示すがfortran intrinsic functionsには結構便利なものがある。数値計算の多くはindex計算だったりする。
  • 大きなdo loopにはラベルだけでなく数字をつけたほうが検索しやすいし視認性もあがる。
  • subroutineのなかにcontainsでsubroutineを入れ込める機能がある。これは変数の受け渡しやスコープをメチャクチャにできるヤバイ機能です。一見便利ですけどやめときましょう(ぼくは引数ゼロの書き出しだけのsubrouitne内subroutineを使っているけれどそのうち廃止したい)。
  • fortranへの標準入力read(,)とかread(5,*)は撲滅しました。getargでいい。
  • intent(inout)な関数は徐々に撲滅する方向で
  • pointer(変数のエイリアス)は活用できる。doの中などで部分配列に対してポインターをあててやるのも良い。
  • HDF5とかXMLとかJSONとかの階層化データフォーマットをfortranで読み書きするのはやりすぎな気がする。そもそもそういうのはだいたいすたれて難儀する。階層化させると柔軟性が乏しくなるしハンドリングも不必要に面倒になる。fortranのunformattedで書けば良い。direct accessも使える。人間が読むところ、gnuplotへ出力するところなどだけformatをつける。
  • 一気にすべてを直すわけにも行かないので進化のさせ方が一番難しいところ。優先順位を考えないといけない。ぼくの場合は、新たな書き直しを半分試験的に埋め込んでいき、しばらく経って(計算に利用して)、問題なければ全面的にその書き直しを採用していく、というようなことをしている。
  • 自分が無知であることを認識する(自戒です)。過去のプログラム履歴を引きずっているのが普通なのですんなりとはいかない。教条主義もよくない。新たに学ばないといけない。それなりの頑固さも必要.それなりの柔軟さも必要.

sample collections

findloc

dim=1書かないと怒られる(gfortran),maxlocなどもある。
多くの部分でindex countを行っているのでこういうのは結構役立つ。論理式書けるので一般的なことができる。

ik3= findloc(kk3,value=kkk3(3),dim=1)

nn2 = findloc([(rsmh2(i,j)>0d0,i=1,nnx)],value=.true.,dim=1,back=.true.)

ispec(j)= findloc([(trim(alabl)==trim(slabl(i)),i=1,nspec)],dim=1,value=.true.)

iout= findloc([(a(i)>0,i=1,iin)],dim=1,value=.true.,back=.true.)-1

昔はこういうのなかったからクソループ書いてました。先生のコードにはクソループいっぱい入ってると思いますよ。もちろんぼくのコードにも。

reshape

zmelc = reshape(dconjg(zmel),shape=shape(zmelc),order=[3,2,1])

zsec= zsec+ matmul(reshape(zmelcww,[ntqxx,nm]),reshape(&
      reshape(zmel,shape=[ns2-ns1+1,ngb,ntqxx],order=[2,1,3]), [nm,ntqxx]))

GPUでもreshapeなんかはサポートしているようだ。再配置(order)などもシステムに任せちゃったほうがいいだろうと推察している。(昔は、subroutineコールで 引数をa(1,1)とするのとa(:,:)とするので驚異的に速度が違ってビックリしたこともあった。いまはmoduleにいれてるし、use
で渡しているのでそういう点にあまり気を使っていない)。

count

nt0  = count(ekc<ef) 

noccx(0:3) = count( ek_(1:nband, 0:3)<efermi,dim=1)

merge

merge,where,count,findlocなどを用いてif文やdo loopはできるだけ書かないこと。

 itini= merge(max(ns1,nt0m+1),  ns1, mask= omg>=ef)

source

mmm='abc'
allocate(farg,source=trim(mmm))

concurrent,forall

シンタックスが違う。

do concurrent (i2 = 1:mnl(ic),i1 = 1:mnl(ic)) 
...
forall(itp=lbound(zsec,1):ubound(zsec,1)) zsec(itp,itp)=1d0)

associate

ポインタ宣言いらなくて値を入れれる。以下のように数式でも可能。
ただ、変数だとポインタ的に振る舞うようだ。
https://www.nag-j.co.jp/fortran/fortran2003/Fortran2003_8_21.html

       do  io1 = 1, norb; if(blks(io1)==0) cycle 
          associate(k1=>ktab(io1),nlm11 => ltab(io1)**2+1)
            sumh = sumh+sum([( &
                 sum(qhh(k1,k2,ilm1,nlm21:nlm22,:)*ppihhz(k1,k2,ilm1,nlm21:nlm22,:)) &
                 ,ilm1=nlm11,nlm11+blks(io1)-1) ])
            do  ilm1 = nlm11, nlm11+ blks(io1)-1
               if( nlm21<= ilm1 .and. ilm1<=nlm21+blks(io2)-1) then
                  sumt = sumt + sum(qhh(k1,k2,ilm1,ilm1,:)*tauhh(k1,k2,ll(ilm1),:))
                  sumq = sumq + sum(qhh(k1,k2,ilm1,ilm1,:)*sighh(k1,k2,ll(ilm1),:))
               endif
            enddo
          endassociate
       enddo

配列構成子

便利なので多用している。sumなどと組み合わせることも多い。

以下のように多次元でも使える場合がある(gfortran,ifort)。matmaは自作の行列掛け算の外部関数。

         call matma(zmelcww,[(transpose(zmel(:,:,itpp)),itpp=lbound(zmel,3),ubound(zmel,3))],zsec,&
              size(zsec,1), size(zmelcww,2)*size(zmelcww,3), size(zsec,2)) !  zsec accumulated

fortranの理屈から言えば使えそうではある。ただ、いまどきのfortranなら怒ってくれてもいいようにも思うー外部関数は徹底的に関知しません、ってことか。(ちょっと非合法っぽいので使用はやめました)。

構造体の利用はむずかしい。

変数の分類学に困り放棄した。

一つの箱にまとめると便利と思うかもしれないが、未来はわからないのでまとめ方がどんどん変わりえる。データ構造は変わり得る。トンカチだけ使いたいのにいつも道具箱を持ちあるかないといけない、今日は薬もついでに入れとくか、ということにもなる。とちゅうで店に寄ってドライバを買って箱に入れるということにもなる。他者(あしたの自分)は「あ、薬が道具箱に入っている!」ということになる。じゃあっていうので薬箱と道具箱を作ってそれを収納する箱を作るとなる… 

また、インデックスシステム(たとえば原子だと、インデックスl,m,nの取りうる値が複雑に依存している)とそれに立脚するデータを別のモジュール(タイプ)に入れたいが、構造体を利用するとなるとムダな混乱が生じる。

電子状態計算では「初期的な環境的変数をもとにbootstrap式に、かなり多様な計算を行っていく」ので、まったくもって不便になりうる。

構造体のインスタンスはサブルーチンの引数として授受することになる。そのようなことをすると不要に長いデータ授受の経路が発生する。その経路を追う、ということが必要になる。もしそのようにせずに構造体のインスタンスを特定のモジュールに入れておいて読み書きするのなら、そもそもそのモジュール変数としておくだけでよい。箱の中の箱に入れなくて良い。結局のところ、構造体には束ねて隠すという本質しかない。関数も入れてクラス化できるというわけだが、おそらくモジュール関数(use onlyでデータにダイレクトにアクセスできる)で間に合うしスッキリする。

pointer配列

ただ、原子ごとに3種の動経方向の電子密度があるような以下場合では、以下のs_rv1のような汎用構造体を利用することができる。構造体をdeallocateするとその中身もメモリリークなく消してくれるようだ(たぶん)。原子ごとに違うメッシュサイズのデータを一括してコントロールできる。

...
use m_struc_def
type(s_rv1) :: rhoat(3,natom)
...
do i=1,natom
  allocate(rhoat(1,i)%v(1:nr(i))) !nrは動経方向のメッシュサイズ
  allocate(rhoat(2,i)%v(1:nr(i)))
  allocate(rhoat(3,i)%v(1:nr(i)))
enddo
使うときは
associate(v1=>rhoat(1,i)%v)
associate(v2=>rhoat(2,i)%v)

...
!========================
module m_struc_def
  type s_rv1
     real(8),allocatable:: v(:)
  endtype s_rv1
...

intentは桁を揃えて書く。

変数宣言とは別にする。use やらimplicitを先に書かないといけない仕様は変だけどしかたない。

module m_rotwave
  public:: rotmto,rotmto2,rotipw,rotipw2,rotwvigg
contains
  !!------------------------------------------------------
  subroutine rotwvigg(igg,q,qtarget,ndimh,napw_in,nband,evec,evecout,ierr)
    intent(in)::      igg,q,qtarget,ndimh,napw_in,nband,evec
    intent(out)::                                            evecout,ierr
...

(としたいが、実際にはuseとimplicit noneの下へ持っていかないと動かない)

(いずれは出力変数はpythonの関数のように左辺に書けるようなるだろうけど。
関数とサブルーチンを区別しなくてもいいだろうし)。

できるだけサイズを渡さない

基本的にモジュール内でsubroutineを定義しているのなら、変数を渡せば、
変数のサイズ情報も受け渡すことができる。

なので以下が機能するには、gtv2_setrcdをモジュール内で定義し、use gtv2_setrcdしてから使う必要がある。

nrecsなどはモジュール変数。
...
  subroutine gtv2_setrcd(recrdin)
    character(len=*):: recrdin(:)
    nrecs = size(recrdin)
    reclnr= len(recrdin(1))   !write(6,*)nrecs,reclnr
    allocate(character(reclnr)::recrd(nrecs))
    recrd=recrdin
  end subroutine gtv2_setrcd

moduleの使い方

  • 以下では初期化m_igv2xall_initでモジュール変数にデータをセットする。
    関数m_Igv2x_setiq(iq)は、出力値をスイッチしてくれるスイッチ関数である。初期化時点でデータをモジュールに溜め込んで、指示変数iqによりm_Igv2x_setiqでスイッチできる。メモリが足りなくなったらあきらめてファイルへ読み書きさせるメカにすることも可能であろう。モジュールを使う側からはそのメカを気にしなくて良いことになる。モジュール内サブルーチンの出力を引数で返すのは人間による整合性の二重記述が発生するのでよろしくない。データをモジュール内に貯め込むというコンセプトは重要であるー外から読めるが外からは書けない。
  • スイッチ関数が不要なばあいも多い。on the flyでデータ生成してモジュール変数に戻すのもあり得る。
module m_igv2x !Return G vectors (integer sets) for given q points specifiec by qplist(:,iq)
  use m_struc_def,only: s_nv2
  public:: m_igv2xall_init, m_igv2x_setiq
  integer,protected,pointer,public :: igv2x(:,:)
  integer,protected,pointer,public :: napw,ndimh,ndimhx
  integer,allocatable,target,protected,public:: ndimhall(:)
  private
  integer,protected,private,target ::napw_z,ndimh_z,ndimhx_z
  type(s_nv2),allocatable,target,protected,private:: igv2xall(:)
  integer,allocatable,target,protected,private:: napwall(:),ndimhxall(:),igv2x_z(:,:)
  logical,private:: init=.true.
contains
  subroutine m_Igv2x_setiq(iq) ! Return G vectors for given qplist(:,iq)
    integer:: iq
    napw  => napwall(iq)
    ndimh => ndimhall(iq)  !nlmto+napw
    ndimhx=> ndimhxall(iq) !nlmto+napw (but x2 when SO=1)
    igv2x => igv2xall(iq)%nv2
  end subroutine m_Igv2x_setiq
  subroutine m_igv2xall_init(iqini,iqend) !initialization for qplist(iqini:iqend)  
    use m_qplist,only: qplist 
    use m_MPItk,only: master_mpi,procid,master
    integer:: iqini,iqend,iq
...
  • またmoduleには引数なしでサブルーチンをcontainするやり方もある。これにはインターフェイス文の自動作成(.mod)という意味がある。変なネット資料だといちいち手でinterfaceを書けと指導しているものもある。複数のサブルーチン場合、何がpublicかを示すという意味もある。引数なしにすることにこだわらないといけない。変なmodule引数があるとどこで読み書きしているかわからなくなる。引数の整合性チェックをある程度してくれるのでとても助かる。optional変数とかname対応をさせたい場合は.modを作るのは必須。

  • これ以外のmoduleの使い方としては「構造体格納module」というのもひとつ用いている。

逆行列計算のコード

moduleにいれてuseするのがいいのかもしれない。そのときにはmatinv(a)でよい。

subroutine matinv(n,a)
  implicit none
  integer :: n, info, ipiv(n)
  real(8):: a(n,n)
  real(8):: work(n*n)
  call dgetrf(n,n,a,n,ipiv,info)
  if(info/=0) then
     print *,' matinv: degtrf info=',info
     call exit(-1) !rx( ' matinv: degtrf ')
  endif
  call dgetri(n,a,n,ipiv,work,n*n,info)
  deallocate(work)
  if(info/=0) then
     print *,'matinv: degtri info=',info
     call exit(-1) !rx( 'matinv: degtri ')
  endif
end subroutine matinv

(rxはプリントしてexit(-1)する自作関数)

逆行列計算のコードその2

以下のような3点内挿を書くとき(wgt3が内挿重みを求める)、3x3逆行列関数inverse33
が必要になった。

              do concurrent(it=itini:itend)     ! nt0p corresponds to efp
          ...
                 associate(x=>we_(it,itp),xi=>freq_r(ixs-1:ixs+1))!x=>we_ is \omega_\epsilon in Eq.(55). 
                   amat(1:3,1)= 1d0                    
                   amat(1:3,2)= xi(1:3)**2
                   amat(1:3,3)= xi(1:3)**4
                   wgt3(:,it,itp)= matmul([1d0,x**2,x**4], inverse33(amat)) 
                 endassociate

というわけで以下のようにしてみた。

  pure function inverse33(matrix) result(inverse) !Inverse of 3X3 matrix
    implicit none
    real(8),intent(in) :: matrix(3,3)
    real(8) :: inverse(3,3), det
    inverse(:,1)= crossf(matrix(:,2),matrix(:,3))
    inverse(:,2)= crossf(matrix(:,3),matrix(:,1))
    inverse(:,3)= crossf(matrix(:,1),matrix(:,2))
    det = sum(matrix(:,1)*inverse(:,1)) 
    inverse = transpose(inverse)
    inverse = inverse/det
  end function inverse33
  pure function crossf(a,b) result(c)
    implicit none
    intent(in):: a,b
    real(8):: a(3),b(3),c(3)
    c(1)=a(2)*b(3)-a(3)*b(2)
    c(2)=a(3)*b(1)-a(1)*b(3)
    c(3)=a(1)*b(2)-a(2)*b(1)
  end function crossf

展開しておいたほうがいいかもしれない。上述の逆行列コードもこういう形式のほうがいい。

compile option

以下は自分のMakeファイルのifort依存部分. m_qplist.f90がコンパイラバグでコケるので、-O0でコンパイルするように指示している。コンパイラ依存をcppなどでソースに書き込むのは長期的に見るとホントしんどくなる。やめたほうがいい。

### Make.ifort.inc
FC = mpiifort
LK = mpiifort
LIBMATH= -mkl  
LIBLOC = ${LIBMATH}
LKFLAGS2 = $(LIBMATH) 
# -check -traceback -debug extended
module = -module $(moddir) 
FFLAGS = -O2       -init:snan $(module)
FFLAGS_LESS = -O0  -init:snan $(module)
FFLAGS_NONE = -O0   -init:snan $(module)
#FFLAGS = -O2 -traceback  -init:snan $(module)
#FFLAGS_LESS = -O0  -traceback -init:snan $(module)
#FFLAGS_NONE = -O0  -traceback -init:snan $(module)
### conditional compile to avoid compilar bug for ucgw 2023feb ifort 18.0.5.274 
$(obj_path)/m_qplist.o: $(subr)m_qplist.f90
	$(FC) $(FFLAGS_NONE) -c $< -o $@

ifortの場合

やはりNaN初期化が安全側だと思う。

### for gfortran ###
mpif90 -Wsurprising -Wmaybe-uninitialized -O2  -g -fimplicit-none -finit-integer=NaN -finit-real=NaN -JOBJ.gfortran -IOBJ.gfortran -c ../main/qg4gw.m.f90 -o OBJ.gfortran/qg4gw.m.o

開発環境

  • fortranでもコード開発の大半は手元のノートパソコン等でもやれる。フロントエンドを共用する大型システムは開発に向かないこともありえる。ぼくはノートパソコン+ubuntu+gfortranでかなりの部分を行っている。最終仕上げを大型へもっていけばいい。
  • ワイドモニタ(A4で4枚分の幅がある)を2画面分割でやるのがよい。中央で割って150文字程度で2画面使える。132文字まで目一杯使ってもコメントが延びたりしうるから。コードの右側に!をつけてコメントを書くとみやすい。ノート+HDMIのワイドモニタ。3モニタは使いきれんかった。

emacs

検索、置換、などに加えて以下の活用がおすすめ。知らない場合は試すべき。

  • etag
  • シェルウインドウのrename,別フレーム。複数のシェルを立ち上げる(ぼくの場合、必ずt1,t2とリネームしている)。
  • ediffモード
  • grepモード
  • コンパイルモードだとM-x compileと打ってコマンドを打つ。たとえば、
Compile command: cd ~/ecalj/SRC/exec;make -j lmf-MPIK lmfa lmchk

などと打つ。タブで次の行。リターンでエラーへジャンプ。C-x b *compileでコンパイル画面に戻ってgで再コンパイル。shift+矢印で記憶が戻る.もちろん、c-x b c tabでcompileに戻れる。

  • 「複数ファイルの一括変換」なども可能。
  • カスタマイズに凝りすぎない。デフォルトを活用。

git,gitk

過去のソースc-x v l,リターン. dで差分表示。
差分のマージにはediffを使っている。
過去のソースcommitを指定してチェックアウト.
gitk --allも有効.

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4