Locked History Actions

Geant4_B1

Geant4 B1 サンプルプログラム

  • exampleB1 をコピーして自分用のプログラムを作成する際のメモ。

シンプルなプログラム

  • ソース

    • Geant4 の構造を理解する過程で、非常にシンプルな(ほとんど何もしない) Geant4 プログラムを作った。テキストベースのユーザーインターフェースは持っているので、NIST のマテリアル情報などは見れる。以下の 2 つのファイルで構成される。
      • CMakeList.txt : cmake コマンドを実行するときに必要なファイル。このファイルを元に Makefile が作られる。

      • exampleSimple.cc : ソース本体。exampleB1 のヘッダファイルとソースファイルから削れそうなものを(おそらく)全部削り、ひとつのファイルにまとめたもの。

  • make & 実行

    1. 上のファイルを置くディレクトリと build に使うディレクトリの 2 つを作る。ここでは、それぞれのディレクトリ名を Simple, Simple-build とする。
    2. CMakeList.txt, exampleSimple.cc を Simple ディレクトリにダウンロード。
    3. Simple-build ディレクトリで cmake コマンドを実行。cmake の引数として CMakeList.txt がある Simple ディレクトリを指定し、オプション : Geant4_DIR で Geant4 をインストールしたディレクトリを指定する (ここでは /home/koba/cern/geant4.9.5.p01/geant4.9.5.p01-install/lib/Geant4-9.5.1 としている)。
    4. cmake 実行後、make。install ディレクトリを指定してないので、make install は行わない。make install をしなくても、プログラムの実行が可能。
    5. Geant4 の 環境変数を設定し(.bashrc などで自動設定していない場合)、exampleSimple を実行。
    6. コマンドプロンプト : idle > が立ち上がるので、ls, cd, help コマンドなどを打ってみる。以下の例では /material/nist/printElement コマンドで Fe の情報を表示している。

    7. exit コマンドで終了
    8. 具体的には、以下のような手順を踏めば良い。
      • $ cd どこかのディレクトリ
        $ mkdir Simple Simple-build
        
        Simple ディレクトリに CMakeList.txt と exampleSimple.cc をダウンロード
        
        $ cd Simple-build
        $ cmake -DGeant4_DIR=/home/koba/cern/geant4.9.5.p01/geant4.9.5.p01-install/lib/Geant4-9.5.1 <Simpleディレクトリのパス>
        $ make
        Scanning dependencies of target exampleSimple
        [100%] Building CXX object CMakeFiles/exampleSimple.dir/exampleSimple.cc.o
        Linking CXX executable exampleSimple
        [100%] Built target exampleSimple
        $ source /home/koba/cern/geant4.9.5.p01/geant4.9.5.p01-install/bin/geant4.sh # 必要であれば
        $ ./exampleSimple
        *************************************************************
         Geant4 version Name: geant4-09-05-patch-01    (20-March-2012)
                              Copyright : Geant4 Collaboration
                              Reference : NIM A 506 (2003), 250-303
                                    WWW : http://cern.ch/geant4
        *************************************************************
        
        <<< Geant4 Physics List simulation engine: QGSP_BIC_EMY 1.1
        
        ### Adding tracking cuts for neutron  TimeCut(ns)= 10000  KinEnergyCut(MeV)= 0
        Idle> cd /material/nist
        Idle> ls
        Command directory path : /material/nist/
        
        
        Guidance :
        Commands for the nist dataBase
        
         Sub-directories : 
         Commands : 
           printElement * print element(s) in dataBase.
           printElementZ * print element Z in dataBase.
           listMaterials * list materials in Geant4 dataBase.
        Idle> printElement Fe
        Nist Element: <Fe>  Z= 26  Aeff(amu)= 55.8451  25 isotopes:
                     N: 45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60  61  62  63  64  65  66  67  68  69  
                  mass(amu): 41930.8 42849.5 43773.6 44693.6 45618.6 46540.2 47466 48389.3 49318.2 50244.4 51174.7 52103 53035 53964.5 54897.5 55828.3 56762.2 57693.8 58628.3 59560.6 60495.8 61428.3 62363.5 63297.3 64233.7 
             abanbance: 0 0 0 0 0 0 0 0 0 0.05845 0 0.91754 0.02119 0.00282 0 0 0 0 0 0 0 0 0 0 0 
        Idle> exit
        $
      • cmake 時のメッセージはこれ

Memos

  • G4UIExecutive は、実行可能な UI が自動で選択されるんだろうか?
  • G4VUserPrimaryGeneratorAction クラスにおいて、G4VUserPrimaryGenerator の具体的なクラスである以下の3つのうちどれか(?)を用いて、粒子を発生させる。
    • G4ParticalGun

    • G4GeneralParticleSource

    • G4HEPEvtInterface
  • G4VPrimaryGeneratorAction の実装 (?) である B1PrimaryGeneratorAction は GeneratePrimaries() を実装する必要があるが、この中で G4LogicalVolumeStore::GetInstance()->GetVolume("Envelope") というメソッドを用いて、B1DetectorConstruction::Construct() で作成したロジカルボリュームのインスタンスを持ってこれる。B1DetectorConstruction::Construct() の後に B1PrimaryGeneratorAction::GeneratePrimaries(G4Event* anEvent) が実行されるため、B1PrimaryGeneratorAction::GeneratePrimaries(G4Event* anEvent) 内でG4LogicalVolumeStore::GetInstance()->GetVolume("Envelope") が実行可能なのだろう。runManager->Initialize(); の実行より前では、おおよそクラスの new しか行われていないため、B1DetectorConstruction::Construct() と B1PrimaryGeneratorAction::GeneratePrimaries(G4Event* anEvent) が実行されるのは runManager->Initialize(); 実行時であると考えられる。

  • 以下の二つの書き方は同等のようだ。
    •   G4int n_particle = 1;
        fParticleGun  = new G4ParticleGun(n_particle);
    •   fParticleGun  = new G4ParticleGun();
    • G4ParticleGun.cc の中身。

    • G4ParticleGun::G4ParticleGun()
      {
        SetInitialValues();
      }
      
      G4ParticleGun::G4ParticleGun(G4int numberofparticles)
      {
        SetInitialValues();
        NumberOfParticlesToBeGenerated = numberofparticles;
      }
      ...
      void G4ParticleGun::SetInitialValues()
      {
        NumberOfParticlesToBeGenerated = 1;
      ...
  • #include "G4NistManager.hh" をインクルードしなくても、/material/nist/printElement Fe などが使える。

  • クラスの new と delete

    • C++ では、 new したクラス (など) は delete しなければならない。しかし Geant4 では、ユーザーが new したクラスをユーザー自身が delete しなくても良い場合がある。それは、ユーザーが new したクラスを Geant4 の特定のクラスのインスタンスに登録する (new したクラスのインスタンスを Geant4 に渡す) 場合である。例えば、G4RunManager のインスタンスに登録した G4VUserPhysicsList のインスタンスはユーザー自身が delete しない。G4RunManager のインスタンスが delete されるときに、そのデストラクタの中で G4VUserPhysicsList のインスタンスも delete される。ただし、G4RunManager のインスタンスはユーザー自身が削除しなければならない。また、ユーザー自身が new し、どこにも登録しない (インスタンス (ポインタ?) をどこにも渡さない) 場合、ユーザーが delete しなければならない。ユーザーが delete しなくても良いインスタンスとして、少なくとも以下のものが挙げられる。

      • G4RunManager のインスタンスに登録する以下のクラスを実装(継承?)したクラスのインスタンス

        • Mandatory User Classes (ユーザーが必ず登録しなければならないクラス)
          • G4VUserDetectorConstruction
          • G4VUserPrimaryGeneratorAction
          • G4VUserPhysicsList
        • Optional User Action Classes (ユーザーが任意に登録するクラス)
          • G4UserSteppingAction

          • G4UserEventAction

          • G4UserRunAction

          • G4UserTrackingAction

          • G4UserStackingAction

    • !G4VUserDetectorConstruction はデストラクタ時、world volume のインスタンスを delete しない。なぜなら、このクラスに登録しているわけではないから。world volume のインスタンスは、!G4VUserDetectorConstruction::Construct() が G4RunManager から呼び出されたときの返り値として G4RunManager に渡され、G4RunManager のデストラクタ時に delete される。

    • Logical volume は G4PVPlacement にインスタンスが渡されるが、G4PVPlacement のデストラクタで delete されているわけではない。Logical volume は G4LogicalVolumeStore というクラスで管理され、ここで delete されているようだ。Physical volume は G4PhysicalVolumeStore で管理されているようだ。

Questions

  • advanced/xray_telescope/include/XrayTelPrimaryGeneratorAction.hh 内で class G4GeneralParticleSource; と class G4Event; という前方宣言がある。前方宣言をする場合としては、ヘッダファイルの相互参照をするときにこの様に書くらしい(参考:C++クラスの書き方が面倒くさい訳)。しかし、参照関係を辿っても相互参照しているわけではないようだ。前方宣言している理由はなんだろう?ヘッダファイルの依存性を減らすためだろうか (参考:ヘッダファイルの依存を減らす)?

    •  * XrayTelPrimaryGeneratorAction.cc
        * #include "G4Event.hh"
        * #include "G4GeneralParticleSource.hh"
        * #include "XrayTelPrimaryGeneratorAction.hh"
      
       * XrayTelPrimaryGeneratorAction.hh
        * #include "G4VUserPrimaryGeneratorAction.hh"
        * class G4GeneralParticleSource;
        * class G4Event;
      
       * G4VUserPrimaryGeneratorAction.hh
        * class G4Event;
      
       * G4GeneralParticleSource.hh
        * #include "globals.hh"
        * #include <vector>
        * #include "G4Event.hh"
        * #include "G4SingleParticleSource.hh"
        * #include "G4GeneralParticleSourceMessenger.hh"
      
       * G4Event.hh
        * #include "globals.hh"
        * #include "G4Allocator.hh"
        * #include "G4PrimaryVertex.hh"
        * #include "G4HCofThisEvent.hh"
        * #include "G4DCofThisEvent.hh"
        * #include "G4TrajectoryContainer.hh"
        * #include "G4VUserEventInformation.hh"
  • ただやっぱり分からないのは、examples/basic/B1/include/B1PrimaryGeneratorAction.hh 内の以下の行である。#include "G4ParticleGun.hh" の後に class G4ParticleGun; が書いてあるが、単に間違いだろうか?それとも深淵な理由があるのだろうか?前方宣言の使いどころが明確に分かるようになれば、これが間違いかどうかもきっと分かるようになるのだろう。

    • #include "G4VUserPrimaryGeneratorAction.hh"
      #include "G4ParticleGun.hh"
      #include "globals.hh"
      
      class G4ParticleGun;
      class G4Event;
      class B1DetectorConstruction;

exampleB1 の 変更

  • 以下の3つ use action の設定をコメントアウトしたら、セグメンテーション違反というメッセージが出た 。(Eclipse で実行する (Ctrl + F11) と、メッセージが出ずに途中で止まる。)
  •   //
      // Stepping action
      runManager->SetUserAction(new B1SteppingAction());
    
      // Event action
      runManager->SetUserAction(new B1EventAction());
    
      // Run action
      runManager->SetUserAction(new B1RunAction());
  • Checking overlaps for volume Envelope ... OK! 
    Checking overlaps for volume Shape1 ... OK! 
    Checking overlaps for volume Shape2 ... OK! 
    セグメンテーション違反です
  • セグメンテーション違反は exampleB1.cc 内の runManager->Initialize(); で起きているもよう。依存関係(?)の問題か? src/*.cc と include/*.hh にインクルードされている *.hh ファイルを確認したところ、B1DetectorConstruction.cc に B1SteppingAction.hh がインクルードされている。そして、Construct() 関数に以下のような箇所がある。

  • G4VPhysicalVolume* B1DetectorConstruction::Construct()
    {
    ...
      B1SteppingAction* steppingAction = B1SteppingAction::Instance(); 
      ////steppingAction->SetVolume(logicShape1);
      steppingAction->SetVolume(logicShape2);
    ...
    }
  • runManager->SetUserAction(new B1SteppingAction()); をコメントアウトしたことによって、 B1SteppingAction のインスタンスが作成されなかったため、この箇所でセグメンテーション違反が発生したと考えられる。本来は B1SteppingAction コンストラクタ (下記) でインスタンスが作成される。

  • B1SteppingAction::B1SteppingAction()
    : G4UserSteppingAction(),
      fVolume(0),
      fEnergy(0.)
    { 
      fgInstance = this;
    }
  • このインスタンスのポインタは静的変数の fgInstance に代入される。静的変数は以下のように static 修飾子を付けて宣言する。静的変数については 静的メンバ が参考になる。

  • class B1SteppingAction : public G4UserSteppingAction
    {
    ...
      private:
        static B1SteppingAction* fgInstance;
    ...
    }
  • 静的変数にアクセスするメソッドも static 修飾子を付けて宣言し (下記参照) 、上記 (B1SteppingAction::Instance(); の部分) のようにクラス名を指定してアクセスしなければならないらしい。この static を使うと、シングルトンができるんだろうか?シングルトンを作るには、排他処理も必要になる?

  • class B1SteppingAction : public G4UserSteppingAction
    {
    ...
        // static access method
        static B1SteppingAction* Instance();
    ...
    }
  • exampleB1.cc 内の runManager->Initialize(); では G4VUserDetectorConstruction::Construct() の実装である B1DetectorConstruction::Construct() が呼び出される。ちなみに runManager は、runManager->SetUserInitialization(new B1DetectorConstruction()); において B1DetectorConstruction クラスのポインタを受け取っている。Geant4 本体のソース (source/run/src/G4RunManager.cc) を読むと、このポインタの delete は runManager のデストラクタ部分で行っている。

  • G4RunManager::~G4RunManager()
    {
      ...
      if(userDetector)
      {
        delete userDetector;
        if(verboseLevel>1) G4cout << "UserDetectorConstruction deleted." << G4endl;
      }
      ...
  • 最初、関数の引数に new したポインタを渡せば、runManager が delete された時に new したポインタも C++ が自動的に削除するのかとも思ったが、ちゃんと runManager のデストラクタで delete されている。ここら辺のことを詳しく明解に理解するには、メモリ管理について知らなければならない? C++ のメモリ管理については C++ クラス設計に関するノート第十一回-02 new 演算子によるメモリの動的確保が参考になりそう。Jave には delete はないが 第十一回-02 new 演算子によるメモリの動的確保 によると Java を使う人でも「メモリ管理の知識は最低限身につけて欲しい」らしい。

名前の変更

  • サンプルプログラム geant4.9.5.p01/examples/basic/B1 を make する際、自分のホームディレクトリのどこかに B1ディレクトリをコピーし、さらに build 用の ディレクトリ (B1-build など) を作成する。ここで、コピーされたディレクトリの名前は B1 である必要はない。また、build 用のディレクトリの名前も B1-build である必要はない。
  • コピーされた B1 ディレクトリ内の src ディレクトリにあるファイル名も .cc という拡張子が付いてさえいればなんでもよい。make コマンド実行時には src ディレクトリ内の .cc が付いているファイルを make している。一方、include ディレクトリ内のインクルードファイルの名前 (*.hh) は *.cc 内で include されているため、インクルードファイルの名前を変えた場合は、*.cc 内の include の部分を書き換える必要がある。この部分を書き換えれば make 出来るが *.cc 内の初めのコメントには、以下のような行があるため、ついでにこれも書き換えると分かり易い。
  • /// \file B1DetectorConstruction.cc
    /// \brief Implementation of the B1DetectorConstruction class
  • さらに、インクルードファイルの名前を変えた場合、*.hh 内のコメント, #ifdef, #define (下記参照) も変えておいた方が分かり易い。ただし、変えなくても make は出来る。
  • /// \file B1DetectorConstruction.hh
    /// \brief Definition of the B1DetectorConstruction class
    
    #ifndef B1DetectorConstruction_h
    #define B1DetectorConstruction_h 1
  • クラス名はインクルードファイルの名前と対応している方がわかり易い。 sed コマンドを用いて置換すれば良い。ただし、クラス名とファイル名に齟齬があっても make は出来る。
  • $ sed -i "s/B1/PrintData/g" exampleB1.cc
    $ cd src
    $ for file in *.cc;do sed -i "s/B1/PrintData/g" $file; done
    $ cd ../include
    $ for file in *.hh;do sed -i "s/B1/PrintData/g" $file; done
  • exampleB1.cc の名前を変えた場合、同じディレクトリ内 にある CMakeList.txt の以下の行の exampleB1.cc を変える。さらに分かり易さの為、CMakeList.txt 内の target 名 : exampleB1 の部分も変えておく。さらに、プロジェクト名 B1 も変える。ただし、exampleB1.cc を変えるだけで make は通る。
  • add_executable(exampleB1 exampleB1.cc ${sources} ${headers})
  • まとめ1

    • まとめると、以下のコマンドを実行すればよい。ここでは、B1 のサンプルプログラムを ProgramName という名前にしている。exeProgramName という名前があまり美しくないけれども、これを ProgramName にすると、Project 名を変えないといけないかも?

    • $ ls
      B1
      $ mv B1 ProgramName
      $ cd ProgramName
      $ for file in exampleB1*; do mv $file ${file/exampleB1/exeProgramName};done
      $ sed -i "s/exampleB1/exeProgramName/g" CMakeLists.txt
      $ for file in src/*.cc include/*.hh; do mv $file ${file/B1/ProgramName};done
      $ for file in src/*.cc include/*.hh exeProgramName.cc CMakeLists.txt; do sed -i "s/B1/ProgramName/g" $file; done
  • まとめ2

    • 実行形式を exeProgramName ではなく、ProgramName という名前にした。以下の手順を踏めばよい。Project の名前は ProjectName である。ProjectName と ProgramName を同じにすると make できない (依存関係の問題か?)。

    • $ ls
      B1
      $ mv B1 ProgramName
      $ cd ProgramName
      $ for file in exampleB1*; do mv $file ${file/exampleB1/ProgramName};done
      $ sed -i "s/exampleB1/ProgramName/g" CMakeLists.txt
      $ sed -i "s/EXAMPLEB1/PROGRAMNAME/g" CMakeLists.txt
      $ sed -i "s/B1/ProjectName/g" CMakeLists.txt
      $ for file in src/*.cc include/*.hh; do mv $file ${file/B1/ProgramName};done
      $ for file in src/*.cc include/*.hh ProgramName.cc; do sed -i "s/B1/ProgramName/g" $file; done