研究室トップ / 真貝のトップ / サイトマップ

Cactusを使った,オリジナルな数値計算コードの作成手順

2002/10/31 update
2002/10/29 update

Cactusコードには,(全部英文だが)100ページ以上に及ぶマニュアルが添付されている. ここでは,それを翻訳する気はないが,初心者の視点から,コメントを補足しておく.
Cactusコードの紹介(ダウンロード方法・テストコンパイルと実行方法)は,別のページを参考のこと.
Contents
  1. まず知っておくこと
  2. 必要最小限なthornは何か
  3. make newthornとcclファイル
  4. プログラミングの実際(0)異なるthorn間のaccess level
  5. プログラミングの実際(1)CCTK_ARGUMENTS
  6. プログラミングの実際(2)グリッドを支配する量
  7. プログラミングの実際(3)他のthornのsubroutineをcallする
  8. プログラミングの実際(4)他のthornのmoduleを使う
とりあえずここまで.
  1. まず知っておくこと

    • Cactusディレクトリで,make UsersGuideとすると,マニュアルが自動生成 される(LaTeX, dvipsなどがインストールされていることが必要).make helpとすると, makeの他のoptionが表示される.
    • Cactusでは,プログラムのグループをthornと呼び, さらにそのグループをarrangementと呼ぶ. 例えば Cactus/arrangements の下には,CactusBaseというarrangementがあり,その下にはCartGrid3Dというthornがある. 実際のプログラムは,例えば,CartGrid3D/srcの下にある. thornやarrangementsを超えて,相互にプログラムをcallして使うことが可能である.
    • Cactusコードは,自らがサポートする機能をCCTK (Cactus Code Toolkit)と呼ぶ.
    • プログラム全体を流れる3次元行列 A(i,j,k) のことを,GridFunctionと呼ぶ. Cactusの定義に従って,GridFunctionを定義しておけば,それらは並列プログラミングに おいて,syncronizeやdata共有が,CCTK_Functionsを用いて簡単に可能となる.データの 取り出しにもMPIを意識する必要がない.
    • 各自のプログラムはCで書いてもFortranで書いても良い.混在していても良い. srcディレクトリの中のmake.code.defnの ファイルに,プログラムをxxx.cあるいはxxx.fあるいはxxx.F90と登録するだけで,コンパイルは自動 に行われる.以下の説明は,Fortran90を例に挙げているが,Cの場合,argumentsの呼び方が若干 異なるので注意.


  2. 必要最小限なthornは何か

    CVSでダウンロードすると, Cactus/arrangementsの中にいろいろ存在するが,本当に 残しておかなければならない便利なthornは,次のもの.

    # arrangement/thorn                # implements (inherits) [friend] {shares}
    #
    CactusBase/Boundary                # boundary (grid) [ ] { }
    CactusBase/CartGrid3D              # grid ( ) [ ] {driver}
    CactusBase/IOASCII                 # IOASCII (IO,hyperslab) [ ] {IO}
    CactusBase/IOBasic                 # IOBasic (IO) [ ] {IO}
    CactusBase/IOUtil                  # IO ( ) [ ] { }
    CactusBase/Time                    # time ( ) [ ] { }
    CactusPUGH/PUGH                    # driver (Cactus) [ ] {cactus}
    CactusPUGH/PUGHReduce              # reduce (driver) [ ] { }
    CactusPUGH/PUGHSlab                # Hyperslab (Driver) [ ] { }
    
    これらは,座標を張るGridFunctionを定義したり,時間発展に必要な基本量を定義していたりする.


  3. make newthornとcclファイル

    • 新しいプログラムを作ろうとするときの第一歩は,Cactusディレクトリの下で
      make newthorn
      
      とすることである.thornの名前を何にするか,どこのarrangementに所属するthornにするかを 聞かれる.この名前は自由である. arrangementsを超えて,相互にプログラムを使うことが可能であるので,自分で解りやすいものに しておけばよい.ここでは,
      arrangements名 = GR
      thorn名    = GRmain
      
      としておこう.
    • make newthornで,自動的に作られるファイルが,いくつかある.まず重要なのは次の3つ.
      GR/GRmain/interface.ccl
      GR/GRmain/param.ccl
      GR/GRmain/schedule.ccl
      
      である.CCLとは,Cactus Configulation Languageの頭文字だそうだ. これらのファイルの役割を簡単に説明しておく.

      • interface.ccl
        このファイルの役割は次の3つ.
        1. thornを代表させる名前(implements: xxx)の登録.この名前が将来にわたって, GridFunctionのsynchronizationや,パラメータファイルでの記述に関わってくる.thornの名前と 同じでも良いし,違っても良い.
        2. 他のどのthornと共通にgrid functionを共有するかの登録 (inherits: boundary Grid xxx , friend: xxx) これらのアクセスレベルに関しては,下記に補足あり.
        3. 利用する GridFunction xxx(i,j,k)の定義.
        このthornが,必ず必要とするthornをinheritsとして登録しておくと, これらがコンパイル時に欠けているとコンパイルエラーとして警告することが可能になる. GridFunctionは,ここで定義しても,actibate(storage/allocate)させない限り, それらはsize (1,1,1)として 登録されているだけなので,メモリを無駄に消費することはない. ここで定義されたGridFunctionは,inherits/friendsで定義された他のthornのGridFunctionと共に, 後に各プログラムで,自動的にsubroutine間の 引き渡し変数(CCTK_ARUGUMENTS)として定義されることになる.
      • param.ccl
        このthornで使うパラメータを定義する.新たに定義しても良いし,他のthornで定義されたパラメータを 拡張定義してもよい.ここで定義されたパラメータは,後に各プログラムで,自動的にCCTK_PARAMETERS として(moduleとして) 定義されることになる.
      • schedule.ccl
        grid functionをいつactibate(storage/allocate)させるか,の定義を行う.これらは,パラメータで on/off してもよいし,どのsubroutineを呼ぶときにon/offするか,として設定しても良い. また,このファイルには, GR/GRmain/src/内の個々のsubroutineを,実行時にいつ呼び出すかを記入する. 「いつ」かは,Cactus内ですでに決められていて,
        CCTK_STARTUP
        CCTK_PARAMCHECK
        CCTK_BASEGRID
        CCTK_INITIAL
        CCTK_PRESTEP
        CCTK_EVOL
        CCTK_POSTSTEP
        CCTK_ANALYSIS
        
        などの「時刻」があらかじめ設定されている.かならずしもすべてのsubroutineについて,この 設定をする必要はなく,あるsubroutineが他をcallすれば,それで済む.Cactusの思想としては, 一つのthornは,上記の「時刻」の「どこかひとつ」に属すること,というのが原則である. この原則を守れば,引き渡し変数(arguments)の不一致でbus errorが発生することが防げるが, 必ずしもこの原則を守る必要もない.
    • make newthornで自動的に作られるディレクトリは,
      GR/GRmain/src
      GR/GRmain/par
      GR/GRmain/doc
      GR/GRmain/test
      
      の4つである.srcの下にはプログラムを収納,parはパラメータファイルのサンプルを作って 入れて置くところ.docは,マニュアルを入れておく所, testは,計算結果のサンプルデータを入れるところ.testにデータを入れておくと, 後にdebugするときに,以前と同じ結果を完全に再現するかどうかのcheckを簡単に行うことが できるが,必ずしも必要ではない.

  4. プログラミングの実際(0)異なるthorn間のaccess level

    thorn間で,変数(GridFunctions)やパラメータを共有する場合には,次のaccess levelの違いが ある.

    thorn A 内の interface.ccl で定義された,次のレベルのGridFunctionについて説明する.

    public:     CCTK_REAL geometry type=gf { gxx gxy gxz gyy gyz gzz } "metric"
    protected:  CCTK_REAL energy   type=gf { energy momentumx } "energy term"
    private:    CCTK_REAL stock    type=gf { stock1 stock2 stock3 } "temporal stock"
    
    public登録されたものは,他のthorn Bで,thorn A がinherit指定されていれば,自動的にそのthorn B内でも 共通にデータを持ったまま利用できる.
    protected登録されたものは,他のthorn Cで,thorn A がfriend指定されていれば,thorn C内でも有効である. 他のthorn Dでもfriend指定されていれば,thorn D内でも有効である.
    private登録されたものは,他のthornでは,利用できない.

    同様,thorn A 内の param.ccl で定義された,次のレベルのパラメータについて説明する.

    Global:
    Restricted:
    Private:
    
    Global登録されたものは,すべてのthornで有効になる.Restricted登録されたものは,他のthorn Bのparam.cclで shares: thornA とthorn Aを指定した後にそのパラメータが再宣言(あるいは拡張定義)されていれば, thorn Bでも有効である. private登録されたものは,他のthornでは,利用できない.
  5. プログラミングの実際(1)CCTK_ARGUMENTS

    プログラムをコンパイルするときには,CPP(C PreProcessor)が使われる.つまり,実際の ソースコードは,まずいったんCactusが所有するargumentを使って,書き直される.

    例えば,典型的なFortran90コードのファイルの始まりは次のようになる.

    !-----------------------------------------------------------------------
    #include "cctk.h"
    #include "cctk_Arguments.h"
    #include "cctk_DefineThorn.h"
    #include "cctk_Parameters.h"
    
          subroutine gr_initial (CCTK_ARGUMENTS)
          implicit none
          DECLARE_CCTK_ARGUMENTS
          DECLARE_CCTK_PARAMETERS
          DECLARE_CCTK_FUNCTIONS
          CCTK_REAL one,zero
          integer i,j,k
    
          one =1.0d0
          zero=0.0d0
    !-----------------------------------------------------------------------
    
    • はじめの4行は,CPPを行う時に必要となる呪文である.これらのmacroの始めの2つは必須である.
    • subroutine名の後のargumentにある,CCTK_ARGUMENTSは,CPP実行時に, そのthorn内で定義されているgrid functionを自動的に書き出す.
    • DECLARE_CCTK_ARGUMENTSは,上記のCCTK_ARGUMENTS内の変数定義文に 自動的に置き換わる.
    • DECLARE_CCTK_PARAMETERSは,そのthorn内で定義されているparameterを commonブロックとして 自動的に置き換わる.
    • DECLARE_CCTK_FUNCTIONSは,もし,そのsubroutine内で,Cactusが定義する functionを使う場合に必要となる.
    • CCTK_REAL は,コンパイル時に,そのマシンが好む実数設定(REAL*8 など)に 書き換わる.

    理解するには, 実際に翻訳されたファイルを見て比べるのが,解りやすいと思う.

    例えばCactusWaveのセットを使ってコンパイルをしたとする. configuration名がwaveFだったとしよう.CPP後の実際のファイルは,Cactus/configs/waveF/buildの下に thorn別に収納されており,xxx.f90という名前のファイルがそれに該当する.

    CCTK_ARGUMENTSに関する補足

    実際にどこで,CCTK_ARGUMENTSが定義されているのかを探し回った所,次のようだった. thornの名前が ADMだったとする.

    Cactus/config/config-name/bindings/include/cctk_Arguments.h
    
    のファイルに書かれている通り,
    #define CCTK_FARGUMENTS ADM_FARGUMENTS
    
    と置き換えられており, これは,さらに
    Cactus/config/config-name/bindings/include/ADM_arguments.h
    
    のファイルに書かれている通り,
    #define ADM_FARGUMENTS _CCTK_FARGUMENTS, ADM_PRIVATE_FARGUMENTS, ADM_PROTECTED_FARGUMENTS, ADM_PUBLIC_FARGUMENTS
    
    の要素からなる. 同様に DECLARE_CCTK_ARGUMENTS も
    DECLARE_ADM_FARGUMENTS
    
    に変換されているが,内訳は(ADM_arguments.h:)
    #define DECLARE_ADM_FARGUMENTS _DECLARE_CCTK_FARGUMENTS DECLARE_ADM_PRIVATE_FARGUMENTS DECLARE_ADM_PROTECTED_FARGUMENTS DECLARE_ADM_PUBLIC_FARGUMENTS
    
    である.この中に,_CCTK_FARGUMENTS というargumentが共通に流れていることがわかる. これについて次に補足する.
  6. プログラミングの実際(2)グリッドを支配する量

    サブルーティン間に流れる引数(argument)には,_CCTK_FARGUMENTS というグループの量がある. この中には,Cactusが並列化を行うための引数が流れている.

    例えば,次のようなプログラムの一部から想像されるように,dx,dy,dz,dtなどの基本量の他に localなprocessorそれぞれがもつ,計算グリッドの大きさ cctk_lsh(:) や,隣のprocessorとの overlapするグリッドの数  cctk_nghostzones(:) などがそうである.

    !-----------------------------------------------------------------------
          DECLARE_CCTK_ARGUMENTS
          CCTK_REAL dx,dy,dz,dt
          integer   i,j,k
    
          dx = CCTK_DELTA_SPACE(1)
          dy = CCTK_DELTA_SPACE(2)
          dz = CCTK_DELTA_SPACE(3)
          dt = CCTK_DELTA_TIME
    
          do k = 1+cctk_nghostzones(3), cctk_lsh(3)-cctk_nghostzones(3)
          do j = 1+cctk_nghostzones(2), cctk_lsh(2)-cctk_nghostzones(2)
          do i = 1+cctk_nghostzones(1), cctk_lsh(1)-cctk_nghostzones(1)
    
               alpha(i,j,k)=1.0
    
          end do
          end do
          end do
    !-----------------------------------------------------------------------
    
    ちなみに,ユーザがパラメータファイルで指定するのは,全体のグリッド数であり, パラメータファイルで,例えば
    # parameters in CactusPUGH/PUGH
    driver::global_nx                 = 96
    driver::global_ny                 = 96
    driver::global_nz                 = 96
    
    と入力している部分である.この情報は, cctk_gsh(3) として流れている.並列計算を行う時に,どのprocessorが,どういう gridになるのかは,(デフォルトでは)自動設定である.そのgrid情報が,cctk_lsh(3) だった, というわけである.
  7. プログラミングの実際(3)他のthornのsubroutineをcallする

    パラメータによって,他のthornのsubroutineをcallする場合がよく生じるかもしれない. しかし,利用者によっては,そのthornを含めてまでコンパイルする必要は無いかも知れない. そんな時は,CPPの特徴を活かして,次のように書く.

    !-----------------------------------------------------------------------
    #include "cctk_DefineThorn.h"
    !-----------------------------------------------------------------------
          if (CCTK_EQUALS(lapse_condition, "maximal")) then
    #ifdef GR_LAPSEMAXIMAL
             call maximal_slice(CCTK_FARGUMENTS)
    #else
             call CCTK_WARN (0, "you need thorn GR-lapseMaximal.")
    #endif
          end if
    !-----------------------------------------------------------------------
    
    こうすれば,GR/lapseMaximalという名前のthornをThornListに入れないでコンパイルしても エラーになることはない.実行時にパラメータファイルで lapse_condition = "maximal" とすれば,警告文が出て途中で止まることになる.
  8. プログラミングの実際(4)他のthornのmoduleを使う

    これは,ちょっと高級な技である.thorn_name/src 内のプログラムは,make.code.defn に,列記 することになるが,module部分があって,他のthornで定義されたmoduleを使う時には,コンパイル時に エラーにならないように,コンパイラーに依存性を前もって報告しておかないといけない.

    そのためには,次の3つのファイルが必要である.例えば,thorn Riemannが,thorn GRmain の moduleをcallする場合を示す.

    make.code.deps 中身は次のよう.これは,cut and pasteしておけばよい.

    
    USESTHORNS = GRmain
    
    # Automatically create dependencies for Fortran modules and Fortran includes
    define F90_DEPENDENCIES
    $(F_DEPEND) $(INC_DIRS:%=-I%) $(EXTRA_DEFINES:%=-D%) -DFCODE $< $(F_DEPEND_OUT)
    $(DEPENDENCY_FIXER)
    dir=`pwd`; $(CPP) $(INC_DIRS:%=-I%) $(EXTRA_DEFINES:%=-D%) -DFCODE $< | $(PERL) $(SRCDIR)/depend.pl - $< $(basename $(notdir $<)).F90.o $(SRCDIR)/ ./ .F90 .F90.o $(USESTHORNS:%=$$dir/../%/) >> $@ || { rm $@; exit 1; }
    endef
    

    depend.pl 中身は次のよう.これも,cut and pasteしておけばよい.

    #!/usr/bin/perl -w
    
    # Create dependencies for Fortran 90 "use" and "include" statements
    
    $src = $ARGV[0];
    $srcfile = $ARGV[1];
    $dest = $ARGV[2];
    $srcdir = $ARGV[3];
    $moddir = $ARGV[4];
    $srcsuffix = $ARGV[5];
    $modsuffix = $ARGV[6];
    @otherdirs = @ARGV[7..$#ARGV];
    
    print "# $src¥n";
    print "$dest:";
    
    open (FILE, $src);
    while () {
      if (/^¥s*include¥s*['"]([^'"]+)['"]/i) {
        print " $srcdir$1";
      } elsif (/^¥s*use¥s+(¥w+)/i) {
        $found = 0;
        if (! $found) {
          if (-e "$srcdir$1$srcsuffix") {
    	$found = 1;
    	print " $moddir$1$modsuffix";
          }
        }
        if (! $found) {
          foreach $dir (@otherdirs) {
    	if (-e "$dir$1$modsuffix") {
    	  $found = 1;
    	  print " $dir$1$modsuffix";
    	  last;
    	}
          }
        }
        if (! $found) {
          die "¥nWhile tracing depencencies:¥nFortran module $1 (referenced in file $srcfile) not found.¥nAborting.¥n¥n";
        }
      }
    }
    close (FILE);
    
    print "¥n";
    

    make.configuration.deps 中身は次のよう.一部に,自分の名前Riemannが登場していることに注目.

    USESTHORNS = GRmain
    
    $(CCTK_LIBDIR)$(DIRSEP)libRiemann.a: $(USESTHORNS:%=$(CCTK_LIBDIR)$(DIRSEP)lib%.a)
    
    以上の3つのファイルを src 内に置いておけば,xxx.F90プログラムは,依存性をMakefileに 伝えていることになる.

本ページの,もう少しだけ詳しい解説は,2000年当時,PennState大学でのMayaコード開発時に, 私が書いたマニュアル(英語)にもある.Mayaコード自体は現在入手不能であるが,参考になるかも しれない.(ただし,上記の「プログラミングの実際(4)他のthornのmoduleを使う」に関する 記述は,上記の内容の方が新しい). [ps][tex]
Go back to the top page of or
My current Email address is