geant4代码讲解:basicB1

geant4代码讲解:basicB1

一、头文件

1.ActionInitialization.hh 操作初始化

1.先定义了两个宏,相当于取个小名,简化代码的书写。

#ifndef B1ActionInitialization_h
#define B1ActionInitialization_h 1
//中间写程序巴拉巴拉

#endif

为了避免重复,所以前面强调了一下:如果没有定义B1ActionInitialization_h的宏,那么定义B1ActionInitialization_h的宏为1

2.写一下头文件。

#include "G4VUserActionInitialization.hh"
  1. 因为"G4VUserActionInitialization.hh" 包含了与用户操作初始化相关的声明和定义,所以先在头文件写出来
  2. 文件名的括号中使用双引号表示编译器应该首先在当前源代码文件的目录中查找该头文件,然后才在系统头文件路径中查找。如果使用尖括号(#include <G4VUserActionInitialization.hh>),则编译器将仅在系统头文件路径中查找该头文件。

3.开辟一个命名空间,用来封装一组相关的类、函数、变量等,同时也是为了避免命名出现冲突。

namespace B1
  1. 此处命名一个叫做B1的空间,类、函数和变量将在B1这个命名空间内定义,不会与其他命名空间或全局命名空间中的标识符发生冲突。
  2. 需要访问命名空间中的标识符时,您可以使用作用域解析运算符 ::,例如 B1::SomeClass,来指定标识符所属的命名空间。

4.定义一个类,描述了这个类的公共成员函数以及它们的特性。

class ActionInitialization : public G4VUserActionInitialization
{
  public:
    ActionInitialization() = default;
    ~ActionInitialization() override = default;

    void BuildForMaster() const override;
    void Build() const override;
};
  1. public::这是一个访问控制修饰符,用于定义下面的成员函数和数据成员的可访问性。在这种情况下,public表示下面的成员是公共的,可以从类的外部访问。
  2. ActionInitialization() = default;:这是一个构造函数的声明。它表明类中有一个名为ActionInitialization 的构造函数。= default的含义是使用默认的构造函数实现,这通常表示构造函数不会执行额外的操作,只会执行默认的初始化。这个构造函数可能用于创建类的实例。
  3. ~ActionInitialization() override = default;:这是一个析构函数的声明。它表明类中有一个名为~ActionInitialization 的析构函数,用于释放类的资源。= default表示使用默认的析构函数实现,通常不需要额外的资源清理。
  4. void BuildForMaster() const override;void Build() const override;:这是类中的两个成员函数的声明。这些函数用于初始化模拟实验中的用户操作。void 表示这些函数不返回任何值。const修饰符表示这些函数不会修改类的成员变量。override 关键字表示这些函数是在基类中声明的虚拟函数,这个类要重写(覆盖)基类中的相应函数。

2.DetectorConstruction.hh 探测器构建

#ifndef B1DetectorConstruction_h
#define B1DetectorConstruction_h 1

#include "G4VUserDetectorConstruction.hh"
#include "globals.hh"

//这两行是前置声明,它们告诉编译器这两个类名是存在的,但具体的实现细节将在其他地方定义。
class G4VPhysicalVolume;
class G4LogicalVolume;
//为什么要在这里提前定义,因为!后面!需要!这俩类型的指针!

namespace B1
{
	
	class DetectorConstruction : public G4VUserDetectorConstruction
	{
	  public:
	    DetectorConstruction() = default;
	    ~DetectorConstruction() override = default;
	
	    G4VPhysicalVolume* Construct() override;
	
	    G4LogicalVolume* GetScoringVolume() const { return fScoringVolume; }
	
	  protected:
	    G4LogicalVolume* fScoringVolume = nullptr;
	};

}
#endif
  1. class DetectorConstruction : public G4VUserDetectorConstruction:这里定义了一个名为DetectorConstruction的类,它继承了G4VUserDetectorConstruction。这意味着DetectorConstruction必须实现G4VUserDetectorConstruction类中的虚拟方法。
  2. public::这里开始的是这个类的公共部分。
  3. DetectorConstruction() = default;~DetectorConstruction() override = default;:这是类的构造函数和析构函数的声明。这里使用了= default,意味着编译器将自动生成默认的实现。
  4. G4VPhysicalVolume* Construct() override;:这是一个名为Construct的虚拟函数,它返回一个指向G4VPhysicalVolume对象的指针。因为它被标记为override,所以它必须在基类中有一个同名的虚拟函数。

在C++中,override关键字用于明确指示编译器一个成员函数应当覆盖(即重写)一个基类中的虚拟函数。如果你标记一个函数为override,但它没有成功覆盖基类中的任何虚拟函数,编译器将报错。这是一种很好的安全机制,因为它确保你明确地覆盖了你想覆盖的函数。[下面举个例子说明]

//关于override的例子
class Base {
public:
    virtual void foo();
};
class Derived : public Base {
public:
    void foo() override; // 正确:覆盖了基类的foo函数
    void bar() override; // 错误:基类中没有虚拟函数bar
};
  1. G4LogicalVolume* GetScoringVolume() const { return fScoringVolume; }:这是一个名为GetScoringVolume的成员函数,它返回一个指向fScoringVolume的指针。
  2. protected::这里开始是这个类的保护部分。
  3. G4LogicalVolume* fScoringVolume = nullptr;:这是一个指向G4LogicalVolume类型对象的指针,初始值为nullptr。

怎么看返回值是什么?(指针相关)

在C++中,星号(*)用于声明一个指针变量,它并不意味着类型(如G4VPhysicalVolume或G4LogicalVolume)本身是一个"指针类"。而是说,这个变量(如fScoringVolume或函数返回值)是一个指针,它指向某个特定类型(如G4VPhysicalVolume或G4LogicalVolume)的对象。

具体到给出的代码:

G4VPhysicalVolume* Construct() override;:这里的星号表示Construct函数返回一个指向G4VPhysicalVolume类型对象的指针。

G4LogicalVolume* GetScoringVolume() const { return fScoringVolume; }:同样,这里的星号表示GetScoringVolume函数返回一个指向G4LogicalVolume类型对象的指针。

G4LogicalVolume* fScoringVolume = nullptr;:在这里,星号表示fScoringVolume是一个指向G4LogicalVolume类型对象的指针。

因此,星号在这里是用于声明指针的,它不改变所涉及类型(如G4VPhysicalVolume或G4LogicalVolume)的性质。这些类型本身并不是“指针类”,而是普通的类或类型。星号表示的是变量或函数返回值是这些类型的指针。

套路是一样的,再过一遍流程:
1.定义宏

#ifndef B1ActionInitialization_h
#define B1ActionInitialization_h 1
//中间写程序巴拉巴拉

#endif

2.写个头文件

#include "G4VUserDetectorConstruction.hh"
#include "globals.hh"

3.先定一下两个类,细节后面另说

class G4VPhysicalVolume;
class G4LogicalVolume;

4.开辟一个命名空间B1

namespace B1
{
	//中间写程序巴拉巴拉
}

5.定义一个类class DetectorConstruction ,

class DetectorConstruction : public G4VUserDetectorConstruction
{
	//中间写程序巴拉巴拉
}

这个DetectorConstruction类继承了G4VUserDetectorConstruction类,并且在这个基础上加了内容,内容下面6中展示

6.DetectorConstruction类里面定义了一些另外的内容

  public:
    DetectorConstruction() = default;
    //构造函数被标记为default,所以它会执行编译器默认提供的构造操作,通常是一些基本的初始化。
    
    ~DetectorConstruction() override = default;
    //析构函数当对象的生命周期结束(例如,对象离开其作用域或者明确地被删除)时,析构函数会被自动调用。
    //同样,因为它被标记为default,它会执行编译器默认的析构操作,通常是释放对象所占用的资源。


    G4VPhysicalVolume* Construct() override;
	//这是一个名为Construct的虚拟函数,它返回一个指向G4VPhysicalVolume对象的指针。
	//因为它被标记为override,所以它必须在基类中有一个同名的虚拟函数。
	
    G4LogicalVolume* GetScoringVolume() const { return fScoringVolume; }
    //这是一个名为GetScoringVolume的成员函数,它返回一个指向fScoringVolume的指针。

  protected:
    G4LogicalVolume* fScoringVolume = nullptr;
    //保护部分是一个指向G4LogicalVolume类型对象的指针,初始值为nullptr。

3.EventAction.hh 事件动作类

0.详细代码

#ifndef B1EventAction_h
#define B1EventAction_h 1
//预处理指令,用于防止B1EventAction的头文件被多次包含。
//如果B1EventAction_h没有定义,则定义它,并且代码将继续。这是防止重复包含的常见技巧。

#include "G4UserEventAction.hh"
#include "globals.hh"

/// Event action class
///

namespace B1
{

    class RunAction;

    class EventAction : public G4UserEventAction
    {
      public:
        EventAction(RunAction* runAction);
        ~EventAction() override = default;

        void BeginOfEventAction(const G4Event* event) override;
        void EndOfEventAction(const G4Event* event) override;

        void AddEdep(G4double edep) { fEdep += edep; }

      private:
        RunAction* fRunAction = nullptr;
        G4double   fEdep = 0.;
        //G4double只是一个double的别名
    };

}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

#endif//这行标志着预处理宏#ifndef B1EventAction_h的结束。

1.命名空间

namespace B1
{
  ...
}

表示的是{ }里面的代码都在B1这个空间内,这样在这个{ }里面的类和函数就不会和其他区域里面的类和函数产生命名的冲突(感觉就为了避免起名字冲突像新建了个文件夹一样)

2.类定义

class RunAction;

这一行是一个类的前置声明,RunAction类将在其他地方定义和实现。它在这里被声明是因为EventAction类的构造函数需要一个RunAction类型的指针。

3.EventAction 类

class EventAction : public G4UserEventAction
{
  ...
};

这是一个名为EventAction的类,它从G4UserEventAction类继承,该基类是Geant4中用于自定义事件动作的类。

4.公有成员

public:
  EventAction(RunAction* runAction);//构造函数,需要一个RunAction类型的指针作为参数。
  ~EventAction() override = default;//析构函数,使用了C++11的default关键字,表示使用默认的析构行为。
  
  void BeginOfEventAction(const G4Event* event) override;//重写了基类中的虚函数,用于定义事件开始时的行为。
  void EndOfEventAction(const G4Event* event) override;//重写了基类中的虚函数,用于定义事件结束时的行为。
  
  void AddEdep(G4double edep) { fEdep += edep; }//一个简单的成员函数,用于累加能量沉积(Energy Deposit)。

关于构造函数、析构函数、虚函数、成员函数的详细解释

5.私有成员

private:
  RunAction* fRunAction = nullptr;
  G4double   fEdep = 0.;

RunAction* fRunAction = nullptr;: 一个指向RunAction类的私有指针,初始化为nullptr
G4double fEdep = 0.;: 一个用于存储能量沉积(Energy Deposit)的私有变量,类型为G4double(这通常是一个double的别名),初始化为0。

4.PrimaryGeneratorAction.hh 主要的生成器动作类

0.详细代码

namespace B1
{

    class PrimaryGeneratorAction : public G4VUserPrimaryGeneratorAction
    {
      public:
        PrimaryGeneratorAction();
        ~PrimaryGeneratorAction() override;

        // method from the base class
        void GeneratePrimaries(G4Event*) override;

        // method to access particle gun
        const G4ParticleGun* GetParticleGun() const { return fParticleGun; }

      private:
        G4ParticleGun* fParticleGun = nullptr; // pointer a to G4 gun class
        G4Box* fEnvelopeBox = nullptr;
    };

}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

#endif

1.命名空间

namespace B1
{
	...
}

这里使用了B1这个命名空间,用于避免与其他代码中的类和函数命名冲突。

2.类定义和继承

class PrimaryGeneratorAction : public G4VUserPrimaryGeneratorAction

这里定义了一个PrimaryGeneratorAction的类,继承的是G4VUserPrimaryGeneratorAction类。

3.构造函数和析构函数

public:
    PrimaryGeneratorAction();
    ~PrimaryGeneratorAction() override;

析构函数为什么写个override?
因为析构函数在基类中已经被声明过了,在这里被重写。

4.成员函数

    // method from the base class
    void GeneratePrimaries(G4Event*) override;

这里重写了基类G4VUserPrimaryGeneratorAction中的GeneratePrimaries虚函数。该函数接受一个G4Event指针作为参数。

    // method to access particle gun
    const G4ParticleGun* GetParticleGun() const { return fParticleGun; }

5.私有成员变量

private:
    G4ParticleGun* fParticleGun = nullptr; // pointer to a G4 gun class
    G4Box* fEnvelopeBox = nullptr;

这里定义了两个私有成员变量。
fParticleGun是一个指向G4ParticleGun类的指针,初始化为nullptr。这可能是用于粒子发射的“枪”。
fEnvelopeBox是一个指向G4Box类的指针,也初始化为nullptr。这可能是用于模拟的几何体。

6.预处理宏的结束

#endif

5.RunAction.hh 运行类

它根据通过步进(stepping)和事件(event)动作累积的能量沉积来计算选定体积内的剂量(dose)。计算出的剂量随后会被打印到屏幕上。

0.详细代码

#ifndef B1RunAction_h
#define B1RunAction_h 1

#include "G4UserRunAction.hh"
#include "G4Accumulable.hh"
#include "globals.hh"

class G4Run;

/// Run action class
///
/// In EndOfRunAction(), it calculates the dose in the selected volume
/// from the energy deposit accumulated via stepping and event actions.
/// The computed dose is then printed on the screen.

namespace B1
{

    class RunAction : public G4UserRunAction
    {
      public:
        RunAction();
        ~RunAction() override = default;

        void BeginOfRunAction(const G4Run*) override;
        void   EndOfRunAction(const G4Run*) override;

        void AddEdep (G4double edep);

      private:
        G4Accumulable<G4double> fEdep = 0.;
        G4Accumulable<G4double> fEdep2 = 0.;
    };

}

#endif

1.预处理宏

#ifndef B1RunAction_h
#define B1RunAction_h 1

这两行是预处理宏,用于确保B1RunAction这个头文件只被包含一次,防止重复定义。

2.头文件引用

#include "G4UserRunAction.hh"
#include "G4Accumulable.hh"
#include "globals.hh"

这些是包含头文件的指令。G4UserRunAction.hhG4Accumulable.hh 是Geant4的头文件,globals.hh 可能是一些全局变量或者设置。

3.类前置声明

class G4Run;

这是对G4Run类的前置声明。这意味着G4Run是一个在别处定义的类,但在这里被引用。

4.命名空间和类定义

namespace B1
{
    class RunAction : public G4UserRunAction
}

5.公开成员函数和构造/析构函数

public:
	RunAction();
	~RunAction() override() = default;
	void BeginOfRunAction(const G4Run*) override;
	void   EndOfRunAction(const G4Run*) override;
	void AddEdep (G4double edep);

这里定义了构造函数、析构函数以及几个成员函数,包括BeginOfRunActionEndOfRunAction,这两个函数在模拟的开始和结束时分别被调用。AddEdep(G4double edep)函数用于添加能量沉积。

6.私有成员变量

private:
	G4Accumulable<G4double> fEdep = 0.;
	G4Accumulable<G4double> fEdep2 = 0.;

在C++中,当涉及到数字的初始化或赋值,数字的表示形式(如 0 与 0.)可能会导致不同的数据类型。具体地说:
0 是一个整数(integer)常量。
0. 或 0.0 是一个浮点数(floating-point)常量。
G4Accumulable 中的模板参数 G4double 是一个代表双精度浮点数的类型,类似于C++的 double。

7.预处理宏结束

#endif

6.SteppingAction.hh 停止动作类

0.详细代码

#ifndef B1SteppingAction_h
#define B1SteppingAction_h 1

#include "G4UserSteppingAction.hh"
#include "globals.hh"

class G4LogicalVolume;

/// Stepping action class
///

namespace B1
{

	class EventAction;
	
	class SteppingAction : public G4UserSteppingAction
	{
	  public:
	    SteppingAction(EventAction* eventAction);
	    ~SteppingAction() override = default;
	
	    // method from the base class
	    void UserSteppingAction(const G4Step*) override;
	
	  private:
	    EventAction* fEventAction = nullptr;
	    G4LogicalVolume* fScoringVolume = nullptr;
	};

}


#endif

1.预处理宏和头文件引用

#ifndef B1SteppingAction_h
#define B1SteppingAction_h 1

#include "G4UserSteppingAction.hh"
#include "globals.hh"

2.类前置声明

class G4LogicalVolume;

G4LogicalVolume在别处定义在此处引用。

3.命名空间和类定义

namespace B1
{
	...
}

这里就是为了避免冲突,开辟一个空间取一个新名字。

class EventAction;
class SteppingAction : public G4UserSteppingAction

这里提前声明了一下class EventAction;是因为后面会用到EventAction类型的指针。
SteppingAction继承的是G4UserSteppingAction

关于继承(公共成员、保护成员、私有成员)
class Base {
private:
    int privateVar;
protected:
    int protectedVar;
public:
    int publicVar;
};

class Derived : public Base {
public:
    void function() {
        // privateVar = 10;  // 错误:不能访问私有成员
        protectedVar = 20;  // 正确:可以访问保护成员
        publicVar = 30;     // 正确:可以访问公共成员
    }
};
  • 在上面的例子中,虽然Derived类继承了Base类的所有成员,但它只能直接访问protectedpublic成员。
  • 子类可以通过被继承类的公共或保护方法间接访问或修改私有成员。但是直接访问是不被允许的。

4.公开成员函数和构造/析构函数

  public:
    SteppingAction(EventAction* eventAction);
    ~SteppingAction() override = default;

这里定义了一个构造函数和一个析构函数,构造函数接收一个EventAction类型的指针。

void UserSteppingAction(const G4Step*) override;

5.私有成员变量

private:
    EventAction* fEventAction = nullptr;
    G4LogicalVolume* fScoringVolume = nullptr;

6.预处理宏结束

#endif

二、源文件

1.ActionInitialization.cc 操作初始化

0.详细代码

#include "ActionInitialization.hh"
#include "PrimaryGeneratorAction.hh"
#include "RunAction.hh"
#include "EventAction.hh"
#include "SteppingAction.hh"

namespace B1
{
	void ActionInitalization::BuildForMaster() const
	{
		auto runAction = new RunAction;
		SetUserAction(runAction);
	}

	void ActionInitialization::Build() const
	{
		SetUserAction(new PrimaryGeneratorAction);
		
		auto runAction = new RunAction;
		SetUserAction(runAction);
	
		auto eventAction = new EventAction(runAction);
		
		SetUserAction(new SteppingAction(eventAction));
	}

}

1.头文件

#include "ActionInitialization.hh"
#include "PrimaryGeneratorAction.hh"
#include "RunAction.hh"
#include "EventAction.hh"
#include "SteppingAction.hh"

这部分代码包括了其他相关头文件,这些头文件中定义了仿真中将要使用的不同动作类。

2.命名空间

namespace B1
{
	...
}

这行代码声明了一个命名空间B1,所有的类和函数都在这个命名空间下定义,以防止名字冲突。

3.成员函数

void ActionInitialization::BuildForMaster() const
	{
	  auto runAction = new RunAction;
	  SetUserAction(runAction);
	}

ActionInitialization::BuildForMaster() 是在 ActionInitialization 类中定义的一个成员函数。它的任务是为主线程(在多线程环境中,主线程负责协调各工作线程)创建并设置 RunAction 类的实例。这个函数没有返回值,这在 C++ 中表示为返回类型void。

void ActionInitialization::Build() const
	{
	  SetUserAction(new PrimaryGeneratorAction);

	  auto runAction = new RunAction;
	  SetUserAction(runAction);

	  auto eventAction = new EventAction(runAction);
	  SetUserAction(eventAction);

	  SetUserAction(new SteppingAction(eventAction));
	}

在geant4中,我们需要定义一些特殊的类来告诉程序如何模拟粒子如何运动和相互作用。这些类称为“动作类”,每个类都有不同的职责。ActionInitialization类的动作是在模拟开始前,设置所有这些需要的动作类。
Build函数是ActionInitialization类的一个方法。当模拟开始时,geant4会自动调用这个Build()方法来创建和设置所需的动作类。

  • SetUserAction(new PrimaryGeneratorAction);这一行创建一个新的PrimaryGeneratorAction对象。这个对象负责定义模拟中粒子的起始条件,例如粒子是什么、它们在哪里、它们的能量有多大等。
  • auto runAction = new RunAction;创建了一个RunAction的对象,并且将这个新对象的地址保存在一个名为runAction的变量中。这个RunAction对象负责处理模拟开始和结束时的动作,例如计算结果和输出数据。
  • SetUserAction(runAction);这一行告诉geant4使用刚刚创建的runAction作为模拟的一部分。
  • auto eventAction = new EventAction(runAction);创建了一个新的EventAction对象,并将runAction对象传递给它,EventAction对象负责管理模拟中单个事件的开始和结束时的动作,例如跟踪每个事件的能量沉积。

传递的意思就是runAction作为参数传递给了 EventAction 的构造函数。

  • 这里的 runAction 是一个指向 RunAction 类型对象的指针。当创建 EventAction 对象时,把这个指针传递给 EventAction 的构造函数,这通常意味着 EventAction 类将以某种方式使用 RunAction 对象。
  • 这不一定意味着 EventAction 可以直接使用 RunAction 中的所有函数和变量。实际情况取决于 RunAction 中的成员(函数和变量)的访问级别(public、protected、private):
  • 如果 RunAction 类的成员是 public 的,那么任何有 RunAction 对象指针的代码都可以直接访问它们。
  • 如果成员是 protected 的,那么只有 RunAction 的子类和友元类可以访问。
  • 如果成员是 private 的,那么只有 RunAction 类自己的成员函数和友元函数/类可以访问它们。
  • SetUserAction(eventAction);告诉geant4使用刚刚创建的对象。
  • SetUserAction(new SteppingAction(eventAction));创建一个新的SteppingAction对象,并将eventAction对象传递给它。SteppingAction对象负责在模拟中粒子每走一步时执行的动作。
解释一下 类

类(Class)在编程中是一种设计工具,用于封装数据(属性)和操作数据的代码(方法或函数)。
在C++中,一个类由以下部分组成:

  • 属性(Attributes): 也被称为数据成员,它们定义了类的状态。属性是分配给类的变量,用于存储信息。
  • 方法(Methods): 方法是类内部定义的函数,它们定义了类的行为。方法可以操作类的属性,或者执行与类相关的任务。

创建一个类时,实际上是在定义一个新的数据类型。通过实例化类,你可以创建对象,每个对象都有其自己的属性集和可以调用的方法。

class Car {
  private:
    int speed; // 属性

  public:
    Car() { speed = 0; } // 构造函数,也是一种特殊的方法

    void accelerate(int increment) { // 方法
        speed += increment;
    }

    int getSpeed() { // 方法
        return speed;
    }
};

int main() {
    Car myCar; // 创建一个Car对象
    myCar.accelerate(10); // 调用方法来改变对象状态
    int currentSpeed = myCar.getSpeed(); // 调用方法来获取信息
    return 0;
}
ActionInitialization.cc和ActionInitialization.hh分别什么作用

.hh文件是对类的声明
.cc文件是对类的定义和方法的实现

类的声明就像声明房间里面有什么人,但是房间里面的人做什么事情,就相当于类的方法和函数,需要.cc里面定义

头文件ActionInitialization.hh

#ifndef B1ActionInitialization_h
#define B1ActionInitialization_h 1

#include "G4VUserActionInitialization.hh"

/// Action initialization class.

namespace B1
{

	class ActionInitialization : public G4VUserActionInitialization
	{
	  public:
	    ActionInitialization() = default;
	    ~ActionInitialization() override = default;
	
	    void BuildForMaster() const override;
	    void Build() const override;
	};

}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

#endif

说了房间里面有 BuildForMaster() ,有Build(),具体他们是什么样,做什么事在.cc里面再说。

源文件ActionInitialization.cc

#include "ActionInitialization.hh"
#include "PrimaryGeneratorAction.hh"
#include "RunAction.hh"
#include "EventAction.hh"
#include "SteppingAction.hh"

namespace B1
{

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

	void ActionInitialization::BuildForMaster() const
	{
	  auto runAction = new RunAction;
	  SetUserAction(runAction);
	}
	
	//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
	
	void ActionInitialization::Build() const
	{
	  SetUserAction(new PrimaryGeneratorAction);
	
	  auto runAction = new RunAction;
	  SetUserAction(runAction);
	
	  auto eventAction = new EventAction(runAction);
	  SetUserAction(eventAction);
	
	  SetUserAction(new SteppingAction(eventAction));
	}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

}

具体定义了BuildForMaster()、Build()是做什么的

  • BuildForMaster() 方法是在并行计算时只在主线程中调用,它只设置那些与整个运行相关的动作,而不是与单个事件或步骤相关的动作。

RunAction:定义在整个运行开始和结束时要执行的操作。

  • Build() 方法是具体负责创建并注册这些动作的方法。

PrimaryGeneratorAction:定义粒子如何在模拟中生成。
RunAction:定义在整个运行开始和结束时要执行的操作。
EventAction:定义每个仿真事件(如粒子通过探测器)开始和结束时要执行的操作。
SteppingAction:定义每个仿真步骤(粒子在探测器中移动的每个小步)时要执行的操作。

2.DetectorConstruction.cc 探测器构建

#include "DetectorConstruction.hh"

#include "G4RunManager.hh"
#include "G4NistManager.hh"
#include "G4Box.hh"
#include "G4Cons.hh"
#include "G4Orb.hh"
#include "G4Sphere.hh"
#include "G4Rrd.hh"
#include "G4LogicalVolume.hh"
#include "G4PVPlacement.hh"
#include "G4SystemOfUnits.hh"

namespace B1
{
	G4VPhysicalVolume* DetectorConstruction::Construct()
	{
		//从NIST物理材料数据库中获取材料管理器的实例
		G4NistManager* nist = G4NistManager::Instance();

		//定义了一个被称为“envelope”的立方体几何体的尺寸和材料(水)
		G4double env_sizeXY = 20*cm, env_sizeZ = 30*cm;
		G4Material* world_mat = nist->FindOrBuildMaster("G4_WATER");

		//设置一个选项,以便在创建几何体时检查它们是否有重叠
		G4bool checkOverlaps = ture;
		
		//这一部分定义了最大的几何体“world”,通常代表整个模拟环境的边界。这里,世界的材料是空气。
      	G4double world_sizeXY = 1.2*env_sizeXY;
      	G4double world_sizeZ  = 1.2*env_sizeZ;
      	G4Material* world_mat = nist->FindOrBuildMaterial("G4_AIR");
	      
		  //创建一个立方体(“World”)的固体
	      auto solidWorld = new G4Box("World",                           // its name
	        0.5 * world_sizeXY, 0.5 * world_sizeXY, 0.5 * world_sizeZ);  // its size
	        
		  //创建一个立方体(“World”)的逻辑体
	      auto logicWorld = new G4LogicalVolume(solidWorld,  // its solid
	        world_mat,                                       // its material
	        "World");                                        // its name
	
		  //创建一个立方体(“World”)的物理体
	      auto physWorld = new G4PVPlacement(nullptr,  // no rotation
	        G4ThreeVector(),                           // at (0,0,0)
	        logicWorld,                                // its logical volume
	        "World",                                   // its name
	        nullptr,                                   // its mother  volume
	        false,                                     // no boolean operation
	        0,                                         // copy number
	        checkOverlaps);                            // overlaps checking
	      
	      // 创建一个较小的立方体(“Envelope”),它被放置在世界内部,并将成为其他更小的形状的容器。
	      auto solidEnv = new G4Box("Envelope",                    // its name
	        0.5 * env_sizeXY, 0.5 * env_sizeXY, 0.5 * env_sizeZ);  // its size
	
	      auto logicEnv = new G4LogicalVolume(solidEnv,  // its solid
	        env_mat,                                     // its material
	        "Envelope");                                 // its name
	
	      new G4PVPlacement(nullptr,  // no rotation
	        G4ThreeVector(),          // at (0,0,0)
	        logicEnv,                 // its logical volume
	        "Envelope",               // its name
	        logicWorld,               // its mother  volume
	        false,                    // no boolean operation
	        0,                        // copy number
	        checkOverlaps);           // overlaps checking

      //创建第一个形状,一个圆锥形(“Shape1”),
      //并赋予了特定的材料(A-150组织)。
      //接着,将其放置在Envelope内的指定位置。
      G4Material* shape1_mat = nist->FindOrBuildMaterial("G4_A-150_TISSUE");
      G4ThreeVector pos1 = G4ThreeVector(0, 2*cm, -7*cm);

      // Conical section shape
      G4double shape1_rmina =  0.*cm, shape1_rmaxa = 2.*cm;
      G4double shape1_rminb =  0.*cm, shape1_rmaxb = 4.*cm;
      G4double shape1_hz = 3.*cm;
      G4double shape1_phimin = 0.*deg, shape1_phimax = 360.*deg;
      auto solidShape1 = new G4Cons("Shape1", shape1_rmina, shape1_rmaxa, shape1_rminb, shape1_rmaxb,
        shape1_hz, shape1_phimin, shape1_phimax);

      auto logicShape1 = new G4LogicalVolume(solidShape1,  // its solid
        shape1_mat,                                        // its material
        "Shape1");                                         // its name

      new G4PVPlacement(nullptr,  // no rotation
        pos1,                     // at position
        logicShape1,              // its logical volume
        "Shape1",                 // its name
        logicEnv,                 // its mother  volume
        false,                    // no boolean operation
        0,                        // copy number
        checkOverlaps);           // overlaps checking

      //创建第二个形状,一个梯形(“Shape2”),使用不同的材料(紧凑的骨头)
      G4Material* shape2_mat = nist->FindOrBuildMaterial("G4_BONE_COMPACT_ICRU");
      G4ThreeVector pos2 = G4ThreeVector(0, -1*cm, 7*cm);

      // Trapezoid shape
      G4double shape2_dxa = 12*cm, shape2_dxb = 12*cm;
      G4double shape2_dya = 10*cm, shape2_dyb = 16*cm;
      G4double shape2_dz  = 6*cm;
      auto solidShape2 = new G4Trd("Shape2",  // its name
        0.5 * shape2_dxa, 0.5 * shape2_dxb, 0.5 * shape2_dya, 0.5 * shape2_dyb,
        0.5 * shape2_dz);  // its size

      auto logicShape2 = new G4LogicalVolume(solidShape2,  // its solid
        shape2_mat,                                        // its material
        "Shape2");                                         // its name

      new G4PVPlacement(nullptr,  // no rotation
        pos2,                     // at position
        logicShape2,              // its logical volume
        "Shape2",                 // its name
        logicEnv,                 // its mother  volume
        false,                    // no boolean operation
        0,                        // copy number
        checkOverlaps);           // overlaps checking

      // Set Shape2 as scoring volume
      //设置“Shape2”的逻辑体作为计分体积,这意味着模拟中的相互作用将在这里记录。
      fScoringVolume = logicShape2;

      //always return the physical World
      //返回指向“world”物理体的指针,这是模拟中最外层的几何体。
      return physWorld;
    }
}

3.EventAction.cc

#include "EventAction.hh"
#include "RunAction.hh"

#include "G4Event.hh"
#include "G4RunManager.hh"

namespace B1
{
	EventAction::EventAction(RunAction* runAction)
	: fRunAction(runAction)
	{}
	
	void EventAction::BeginOfEventAction(const G4Event*)
	{
	  fEdep = 0.;
	}
	
	void EventAction::EndOfEventAction(const G4Event*)
	{
	  // accumulate statistics in run action
	  fRunAction->AddEdep(fEdep);
	}
}

部分代码解读

EventAction::EventAction(RunAction* runAction)
: fRunAction(runAction)
{}

这是EventAction类的构造函数。它接受一个指向RunAction对象的指针并将其存储在成员变量fRunAction中。这允许EventAction在事件结束时更新RunAction对象的状态。

void EventAction::BeginOfEventAction(const G4Event*)
{
  fEdep = 0.;
}

BeginOfEventAction是在每个事件开始时调用的函数。这里它初始化了fEdep变量,该变量用来累计在此事件中沉积的能量总量为零。fEdep通常是在粒子穿过物质时损失的能量,这是粒子物理实验中的一个重要测量指标。

void EventAction::EndOfEventAction(const G4Event*)
{
  fRunAction->AddEdep(fEdep);
}

EndOfEventAction是在每个事件结束时调用的函数。它调用了RunAction对象的AddEdep方法,将此事件的能量沉积总量fEdep添加到运行统计中。这样,运行的所有事件的能量沉积可以在整个模拟结束时汇总。

4.PrimaryGeneratorAction.cc 主要的生成器动作类

0.代码详解

#include "PrimaryGeneratorAction.hh"

#include "G4LogicalVolumeStore.hh"
#include "G4LogicalVolume.hh"
#include "G4Box.hh"
#include "G4RunManager.hh"
#include "G4ParticleGun.hh"
#include "G4ParticleTable.hh"
#include "G4ParticleDefinition.hh"
#include "G4SystemOfUnits.hh"
#include "Randomize.hh"

namespace B1
{

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

	PrimaryGeneratorAction::PrimaryGeneratorAction()
	{
	  G4int n_particle = 1;
	  fParticleGun  = new G4ParticleGun(n_particle);
	
	  // default particle kinematic
	  G4ParticleTable* particleTable = G4ParticleTable::GetParticleTable();
	  G4String particleName;
	  G4ParticleDefinition* particle
	    = particleTable->FindParticle(particleName="gamma");
	  fParticleGun->SetParticleDefinition(particle);
	  fParticleGun->SetParticleMomentumDirection(G4ThreeVector(0.,0.,1.));
	  fParticleGun->SetParticleEnergy(6.*MeV);
	}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

	PrimaryGeneratorAction::~PrimaryGeneratorAction()
	{
	  delete fParticleGun;
	}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

	void PrimaryGeneratorAction::GeneratePrimaries(G4Event* anEvent)
	{
	  //this function is called at the begining of ecah event
	  //
	
	  // In order to avoid dependence of PrimaryGeneratorAction
	  // on DetectorConstruction class we get Envelope volume
	  // from G4LogicalVolumeStore.
	
	  G4double envSizeXY = 0;
	  G4double envSizeZ = 0;
	
	  if (!fEnvelopeBox)
	  {
	    G4LogicalVolume* envLV
	      = G4LogicalVolumeStore::GetInstance()->GetVolume("Envelope");
	    if ( envLV ) fEnvelopeBox = dynamic_cast<G4Box*>(envLV->GetSolid());
	  }
	
	  if ( fEnvelopeBox ) {
	    envSizeXY = fEnvelopeBox->GetXHalfLength()*2.;
	    envSizeZ = fEnvelopeBox->GetZHalfLength()*2.;
	  }
	  else  {
	    G4ExceptionDescription msg;
	    msg << "Envelope volume of box shape not found.\n";
	    msg << "Perhaps you have changed geometry.\n";
	    msg << "The gun will be place at the center.";
	    G4Exception("PrimaryGeneratorAction::GeneratePrimaries()",
	     "MyCode0002",JustWarning,msg);
	  }

	  G4double size = 0.8;
	  G4double x0 = size * envSizeXY * (G4UniformRand()-0.5);
	  G4double y0 = size * envSizeXY * (G4UniformRand()-0.5);
	  G4double z0 = -0.5 * envSizeZ;
	
	  fParticleGun->SetParticlePosition(G4ThreeVector(x0,y0,z0));
	
	  fParticleGun->GeneratePrimaryVertex(anEvent);
	}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

}

1.头文件

#include "PrimaryGeneratorAction.hh"

#include "G4LogicalVolumeStore.hh"
#include "G4LogicalVolume.hh"
#include "G4Box.hh"
#include "G4RunManager.hh"
#include "G4ParticleGun.hh"
#include "G4ParticleTable.hh"
#include "G4ParticleDefinition.hh"
#include "G4SystemOfUnits.hh"
#include "Randomize.hh"

2.PrimaryGeneratorAction类的定义

PrimaryGeneratorAction::PrimaryGeneratorAction()
{
	//中间写程序巴拉巴拉
}

这就是一个构造函数,里面放了哪些内容?代码如下:

 G4int n_particle = 1;
  fParticleGun  = new G4ParticleGun(n_particle);

  // default particle kinematic
  G4ParticleTable* particleTable = G4ParticleTable::GetParticleTable();
  G4String particleName;
  G4ParticleDefinition* particle
    = particleTable->FindParticle(particleName="gamma");
  fParticleGun->SetParticleDefinition(particle);
  fParticleGun->SetParticleMomentumDirection(G4ThreeVector(0.,0.,1.));
  fParticleGun->SetParticleEnergy(6.*MeV);

这段代码初始化了粒子表的引用,设置了要查找的粒子名称,并获取了该粒子的定义,以便后续在粒子枪 fParticleGun 中使用,发射具有这些特定属性的粒子。详细讲解一下,看一下怎么做到的。

G4int n_particle = 1;

就是定义了一个整数为1,这个整数是做什么的继续往下看。

fParticleGun  = new G4ParticleGun(n_particle);

G4ParticleGun是什么?

  • 是geant4里面已经封装好的一个类,可以直接调用(头文件也有声明#include "G4ParticleGun.hh")它包含了用于粒子发射模拟的所有必要的属性和方法。
  • 这些属性包括粒子的类型、能量、方向等,而方法包括设置这些属性的函数和发射粒子的函数。

那这行代码的意思就是:发射一个粒子(因为n_particle是1)

G4ParticleTable* particleTable = G4ParticleTable::GetParticleTable();
G4String particleName;
G4ParticleDefinition* particle
  = particleTable->FindParticle(particleName="gamma");

先解释一下每一行的语法(为什么是这么写的):

  • G4ParticleTable* particleTable = G4ParticleTable::GetParticleTable();就是创建了一个指针,它指向 G4ParticleTable 类的实例。
  1. G4ParticleTable 是Geant4框架中一个单例类,这意味着在整个程序中只有一个实例。
  2. GetParticleTable() 是该类的一个静态方法,返回指向这个唯一实例的指针。
  3. G4ParticleTable 包含了所有定义的粒子类型,您可以查询和获取不同的粒子属性。
  • G4String particleName;定义了一个 G4String 类型的变量 particleNameG4String 是Geant4中使用的字符串类型,类似于标准C++库中的 std::string。这里,particleName 变量被初始化为空,准备用于指定想要查找的粒子名称。
  • G4ParticleDefinition* particle= particleTable->FindParticle(particleName="gamma");这行代码做了好几件事情
  1. particleName="gamma" 是一个赋值操作,它将 particleName 变量的值设置为 "gamma"。这个字符串代表伽马射线粒子,在Geant4中有特定的内置定义。
  2. particleTable->FindParticle(particleName) 调用 FindParticle 方法,从粒子表 particleTable 中查找名称为 "gamma" 的粒子定义。这个方法返回一个指向找到的粒子定义的指针.
  3. G4ParticleDefinition* particle 定义了一个指针 particle,用于接收 FindParticle 方法返回的粒子定义指针。这样,particle 就指向了伽马粒子的定义,比如它的名称、质量、电荷等。

在这里插入图片描述
接着我们初始化一些信息:

fParticleGun->SetParticleDefinition(particle);
fParticleGun->SetParticleMomentumDirection(G4ThreeVector(0.,0.,1.));
fParticleGun->SetParticleEnergy(6.*MeV);

设置了粒子源:它定义了粒子类型(伽马射线),设置了粒子的发射方向(向z轴正方向),并设置了粒子的能量(6兆电子伏特)。

3.析构函数

PrimaryGeneratorAction::~PrimaryGeneratorAction()
{
  delete fParticleGun;//释放了fParticleGun对象占用的内存
}

负责在PrimaryGeneratorAction对象不再需要时清理内存。
在这个例子中,它删除了之前创建的fParticleGun对象,以避免内存泄漏。

4.GeneratePrimaries函数

void PrimaryGeneratorAction::GeneratePrimaries(G4Event* anEvent)是在每个模拟事件的开始时调用的函数,它负责实际生成粒子。

	void PrimaryGeneratorAction::GeneratePrimaries(G4Event* anEvent)
	{
	  G4double envSizeXY = 0;
	  G4double envSizeZ = 0;
	
	  if (!fEnvelopeBox)
	  {
	    G4LogicalVolume* envLV
	      = G4LogicalVolumeStore::GetInstance()->GetVolume("Envelope");
	    if ( envLV ) fEnvelopeBox = dynamic_cast<G4Box*>(envLV->GetSolid());
	  }
	
	  if ( fEnvelopeBox ) {
	    envSizeXY = fEnvelopeBox->GetXHalfLength()*2.;
	    envSizeZ = fEnvelopeBox->GetZHalfLength()*2.;
	  }
	  else  {
	    G4ExceptionDescription msg;
	    msg << "Envelope volume of box shape not found.\n";
	    msg << "Perhaps you have changed geometry.\n";
	    msg << "The gun will be place at the center.";
	    G4Exception("PrimaryGeneratorAction::GeneratePrimaries()",
	     "MyCode0002",JustWarning,msg);
	  }

	  G4double size = 0.8;
	  G4double x0 = size * envSizeXY * (G4UniformRand()-0.5);
	  G4double y0 = size * envSizeXY * (G4UniformRand()-0.5);
	  G4double z0 = -0.5 * envSizeZ;
	
	  fParticleGun->SetParticlePosition(G4ThreeVector(x0,y0,z0));
	
	  fParticleGun->GeneratePrimaryVertex(anEvent);
	}

具体的有两段if语句
第一段

  G4double envSizeXY = 0;
  G4double envSizeZ = 0;

  if (!fEnvelopeBox)
  {
    G4LogicalVolume* envLV
      = G4LogicalVolumeStore::GetInstance()->GetVolume("Envelope");
    if ( envLV ) fEnvelopeBox = dynamic_cast<G4Box*>(envLV->GetSolid());
  }

这里尝试检索模拟几何结构中的一个特定部分(名为"Envelope"的逻辑体积),以便根据该体积确定粒子发射的位置。

第二段

  if ( fEnvelopeBox ) {
    envSizeXY = fEnvelopeBox->GetXHalfLength()*2.;
    envSizeZ = fEnvelopeBox->GetZHalfLength()*2.;
  }
  else  {
    G4ExceptionDescription msg;
    msg << "Envelope volume of box shape not found.\n";
    msg << "Perhaps you have changed geometry.\n";
    msg << "The gun will be place at the center.";
    G4Exception("PrimaryGeneratorAction::GeneratePrimaries()",
     "MyCode0002",JustWarning,msg);
  }

  G4double size = 0.8;
  G4double x0 = size * envSizeXY * (G4UniformRand()-0.5);
  G4double y0 = size * envSizeXY * (G4UniformRand()-0.5);
  G4double z0 = -0.5 * envSizeZ;

  fParticleGun->SetParticlePosition(G4ThreeVector(x0,y0,z0));

  fParticleGun->GeneratePrimaryVertex(anEvent);

如果找到了"Envelope"体积,就使用它来计算粒子的初始位置。如果没有找到,就输出一个警告。

最后,粒子的位置被随机设置在"Envelope"体积的一定范围内,并通过调用fParticleGun->GeneratePrimaryVertex(anEvent)生成粒子。

中间还有一部分

G4double size = 0.8;
G4double x0 = size * envSizeXY * (G4UniformRand()-0.5);
G4double y0 = size * envSizeXY * (G4UniformRand()-0.5);
G4double z0 = -0.5 * envSizeZ;

fParticleGun->SetParticlePosition(G4ThreeVector(x0,y0,z0));
  • 首先定义了一个名为 size 的变量,值为0.8,它是用来限定粒子发射位置范围的一个系数。
  • 然后使用 G4UniformRand() 函数生成一个[0,1)之间的随机数,通过与 size 及 “Envelope” 尺寸的乘积,计算出粒子在X和Y方向上的随机初始位置。
  • 对于Z方向,它设置粒子的起始位置在"Envelope"区域的底部(这里通过 -0.5 * envSizeZ 计算)。
  • 然后,这个位置被设置为粒子枪的发射位置。
fParticleGun->GeneratePrimaryVertex(anEvent);

5.RunAction.cc

0.详细代码

#include "RunAction.hh"
#include "PrimaryGeneratorAction.hh"
#include "DetectorConstruction.hh"
// #include "Run.hh"

#include "G4RunManager.hh"
#include "G4Run.hh"
#include "G4AccumulableManager.hh"
#include "G4LogicalVolumeStore.hh"
#include "G4LogicalVolume.hh"
#include "G4UnitsTable.hh"
#include "G4SystemOfUnits.hh"

namespace B1
{

	//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
	
	RunAction::RunAction()
	{
	  // add new units for dose
	  //
	  const G4double milligray = 1.e-3*gray;
	  const G4double microgray = 1.e-6*gray;
	  const G4double nanogray  = 1.e-9*gray;
	  const G4double picogray  = 1.e-12*gray;
	
	  new G4UnitDefinition("milligray", "milliGy" , "Dose", milligray);
	  new G4UnitDefinition("microgray", "microGy" , "Dose", microgray);
	  new G4UnitDefinition("nanogray" , "nanoGy"  , "Dose", nanogray);
	  new G4UnitDefinition("picogray" , "picoGy"  , "Dose", picogray);
	
	  // Register accumulable to the accumulable manager
	  G4AccumulableManager* accumulableManager = G4AccumulableManager::Instance();
	  accumulableManager->RegisterAccumulable(fEdep);
	  accumulableManager->RegisterAccumulable(fEdep2);
	}
	
	//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
	
	void RunAction::BeginOfRunAction(const G4Run*)
	{
	  // inform the runManager to save random number seed
	  G4RunManager::GetRunManager()->SetRandomNumberStore(false);
	
	  // reset accumulables to their initial values
	  G4AccumulableManager* accumulableManager = G4AccumulableManager::Instance();
	  accumulableManager->Reset();
	
	}
	
	//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
	
	void RunAction::EndOfRunAction(const G4Run* run)
	{
	  G4int nofEvents = run->GetNumberOfEvent();
	  if (nofEvents == 0) return;
	
	  // Merge accumulables
	  G4AccumulableManager* accumulableManager = G4AccumulableManager::Instance();
	  accumulableManager->Merge();
	
	  // Compute dose = total energy deposit in a run and its variance
	  //
	  G4double edep  = fEdep.GetValue();
	  G4double edep2 = fEdep2.GetValue();
	
	  G4double rms = edep2 - edep*edep/nofEvents;
	  if (rms > 0.) rms = std::sqrt(rms); else rms = 0.;
	
	  const auto detConstruction = static_cast<const DetectorConstruction*>(
	    G4RunManager::GetRunManager()->GetUserDetectorConstruction());
	  G4double mass = detConstruction->GetScoringVolume()->GetMass();
	  G4double dose = edep/mass;
	  G4double rmsDose = rms/mass;
	
	  // Run conditions
	  //  note: There is no primary generator action object for "master"
	  //        run manager for multi-threaded mode.
	  const auto generatorAction = static_cast<const PrimaryGeneratorAction*>(
	    G4RunManager::GetRunManager()->GetUserPrimaryGeneratorAction());
	  G4String runCondition;
	  if (generatorAction)
	  {
	    const G4ParticleGun* particleGun = generatorAction->GetParticleGun();
	    runCondition += particleGun->GetParticleDefinition()->GetParticleName();
	    runCondition += " of ";
	    G4double particleEnergy = particleGun->GetParticleEnergy();
	    runCondition += G4BestUnit(particleEnergy,"Energy");
	  }
	
	  // Print
	  //
	  if (IsMaster()) {
	    G4cout
	     << G4endl
	     << "--------------------End of Global Run-----------------------";
	  }
	  else {
	    G4cout
	     << G4endl
	     << "--------------------End of Local Run------------------------";
	  }
	
	  G4cout
	     << G4endl
	     << " The run consists of " << nofEvents << " "<< runCondition
	     << G4endl
	     << " Cumulated dose per run, in scoring volume : "
	     << G4BestUnit(dose,"Dose") << " rms = " << G4BestUnit(rmsDose,"Dose")
	     << G4endl
	     << "------------------------------------------------------------"
	     << G4endl
	     << G4endl;
	}
	
	//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
	
	void RunAction::AddEdep(G4double edep)
	{
	  fEdep  += edep;
	  fEdep2 += edep*edep;
	}
	
	//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

}

1.定义剂量单位:

首先,代码定义了几个新的剂量单位,它们是灰(Gray)的分数单位,用于精细的剂量测量。格雷是吸收剂量的单位,1格雷等于物质每千克吸收1焦耳的辐射能量。milligray, microgray, nanogray, 和 picogray 分别是格雷的千分之一、百万分之一、十亿分之一和万亿分之一。

const G4double milligray = 1.e-3*gray;
const G4double microgray = 1.e-6*gray;
const G4double nanogray  = 1.e-9*gray;
const G4double picogray  = 1.e-12*gray;

这部分代码创建了这些单位,并与 gray 相乘得到具体的转换系数。

2.注册单位定义:

接下来,代码使用 new G4UnitDefinition 创建了这些单位的定义。这些定义在Geant4的单位系统中注册了新的剂量单位,以便在模拟输出中使用。

new G4UnitDefinition("milligray", "milliGy" , "Dose", milligray);
new G4UnitDefinition("microgray", "microGy" , "Dose", microgray);
new G4UnitDefinition("nanogray" , "nanoGy"  , "Dose", nanogray);
new G4UnitDefinition("picogray" , "picoGy"  , "Dose", picogray);

3.注册累积变量:

构造函数获取 G4AccumulableManager 的实例,并注册了两个累积变量 fEdepfEdep2。累积变量用于在模拟过程中累加值,如能量沉积,它们在多线程模式下特别有用,因为它们可以在所有线程间合并。

G4AccumulableManager* accumulableManager = G4AccumulableManager::Instance();
accumulableManager->RegisterAccumulable(fEdep);
accumulableManager->RegisterAccumulable(fEdep2);

4.BeginOfRunAction()成员函数

void RunAction::BeginOfRunAction(const G4Run*)
{
  // inform the runManager to save random number seed
  G4RunManager::GetRunManager()->SetRandomNumberStore(false);

  // reset accumulables to their initial values
  G4AccumulableManager* accumulableManager = G4AccumulableManager::Instance();
  accumulableManager->Reset();

}

BeginOfRunAction 函数为模拟运行的开始做准备工作,设置了随机数种子不被保存以确保模拟的随机性,并重置了累积变量以便新的运行可以从干净的状态开始。

  • G4RunManager::GetRunManager() 调用返回指向当前 G4RunManager 实例的指针。G4RunManager 是Geant4中负责管理模拟运行的类的实例。
  • SetRandomNumberStore(false)G4RunManager 类的方法,它设置了是否保存随机数种子的标志。在这个情况下,参数被设置为 false,意味着模拟运行的随机数种子不会被保存。这通常用于确保每次模拟运行都能产生不同的随机数序列,从而模拟不确定性。如果想要重现特定的模拟结果,可以将此设置为 true 并提供一个种子。
  • G4AccumulableManager::Instance() 调用返回指向当前 G4AccumulableManager 实例的指针。G4AccumulableManager 是Geant4中用于管理可累积变量的类的实例。可累积变量是指在模拟中会被不断累加的量,例如能量沉积。
  • Reset() 方法重置了所有已注册的可累积变量的值到它们的初始状态。这是在每次运行开始时进行的,以确保之前运行的数据不会影响到新的运行。

5.EndOfRunAction()

void RunAction::EndOfRunAction(const G4Run* run)
{
  G4int nofEvents = run->GetNumberOfEvent();
  if (nofEvents == 0) return;

  // Merge accumulables
  G4AccumulableManager* accumulableManager = G4AccumulableManager::Instance();
  accumulableManager->Merge();

  // Compute dose = total energy deposit in a run and its variance
  //
  G4double edep  = fEdep.GetValue();
  G4double edep2 = fEdep2.GetValue();

  G4double rms = edep2 - edep*edep/nofEvents;
  if (rms > 0.) rms = std::sqrt(rms); else rms = 0.;

  const auto detConstruction = static_cast<const DetectorConstruction*>(
    G4RunManager::GetRunManager()->GetUserDetectorConstruction());
  G4double mass = detConstruction->GetScoringVolume()->GetMass();
  G4double dose = edep/mass;
  G4double rmsDose = rms/mass;

  // Run conditions
  //  note: There is no primary generator action object for "master"
  //        run manager for multi-threaded mode.
  const auto generatorAction = static_cast<const PrimaryGeneratorAction*>(
    G4RunManager::GetRunManager()->GetUserPrimaryGeneratorAction());
  G4String runCondition;
  if (generatorAction)
  {
    const G4ParticleGun* particleGun = generatorAction->GetParticleGun();
    runCondition += particleGun->GetParticleDefinition()->GetParticleName();
    runCondition += " of ";
    G4double particleEnergy = particleGun->GetParticleEnergy();
    runCondition += G4BestUnit(particleEnergy,"Energy");
  }

  // Print
  //
  if (IsMaster()) {
    G4cout
     << G4endl
     << "--------------------End of Global Run-----------------------";
  }
  else {
    G4cout
     << G4endl
     << "--------------------End of Local Run------------------------";
  }

  G4cout
     << G4endl
     << " The run consists of " << nofEvents << " "<< runCondition
     << G4endl
     << " Cumulated dose per run, in scoring volume : "
     << G4BestUnit(dose,"Dose") << " rms = " << G4BestUnit(rmsDose,"Dose")
     << G4endl
     << "------------------------------------------------------------"
     << G4endl
     << G4endl;
}

解释下各部分的作用:
1.获取事件数量:

G4int nofEvents = run->GetNumberOfEvent();
  if (nofEvents == 0) return;

2.合并累积变量:

  G4AccumulableManager* accumulableManager = G4AccumulableManager::Instance();
  accumulableManager->Merge();

3.计算剂量和其方差:

G4double edep  = fEdep.GetValue();
G4double edep2 = fEdep2.GetValue();

G4double rms = edep2 - edep*edep/nofEvents;
if (rms > 0.) rms = std::sqrt(rms); else rms = 0.;

4.获取探测器构造和评分体积的质量:

const auto detConstruction = static_cast<const DetectorConstruction*>(
  G4RunManager::GetRunManager()->GetUserDetectorConstruction());
G4double mass = detConstruction->GetScoringVolume()->GetMass();

5.计算并打印剂量信息:

G4double dose = edep/mass;
G4double rmsDose = rms/mass;

6.输出运行条件和结果:

const auto generatorAction = static_cast<const PrimaryGeneratorAction*>(
    G4RunManager::GetRunManager()->GetUserPrimaryGeneratorAction());
  • 这行代码从Geant4的运行管理器(G4RunManager)获取用户定义的初级生成器动作(PrimaryGeneratorAction)对象,并将其转换为正确的类型。
  • static_cast是C++中的类型转换运算符,用于在相关类型之间进行转换。这里它将G4VUserPrimaryGeneratorAction*类型转换为PrimaryGeneratorAction*类型。
G4String runCondition;
if (generatorAction)
{
  const G4ParticleGun* particleGun = generatorAction->GetParticleGun();
  runCondition += particleGun->GetParticleDefinition()->GetParticleName();
  runCondition += " of ";
  G4double particleEnergy = particleGun->GetParticleEnergy();
  runCondition += G4BestUnit(particleEnergy,"Energy");
}
  • 如果获取到的初级生成器动作对象(generatorAction)不是空指针,则代码会进一步获取与之关联的粒子枪(G4ParticleGun)。
  • 然后,它构建一个描述模拟运行条件的字符串runCondition,包括粒子的名称和能量。G4ParticleDefinition提供了正在发射的粒子的详细信息,而G4BestUnit是一个用于格式化输出,将能量值转换为最合适的单位(如MeV、GeV等)的函数。
if (IsMaster()) {
  G4cout
   << G4endl
   << "--------------------End of Global Run-----------------------";
}
else {
  G4cout
   << G4endl
   << "--------------------End of Local Run------------------------";
}

接下来的代码块检查当前RunAction对象是否属于主线程。如果是(在多线程模式下的“主”线程),它将输出一条表示全局运行结束的消息。如果不是(在多线程模式下的一个“局部”或“辅助”线程),它将输出一条表示局部运行结束的消息。

G4cout
   << G4endl
   << " The run consists of " << nofEvents << " "<< runCondition
   << G4endl
   << " Cumulated dose per run, in scoring volume : "
   << G4BestUnit(dose,"Dose") << " rms = " << G4BestUnit(rmsDose,"Dose")
   << G4endl
   << "------------------------------------------------------------"
   << G4endl
   << G4endl;

最后,代码输出模拟运行的统计信息。它打印出了模拟中发生的事件数量(nofEvents),运行条件(粒子类型和能量),以及在评分体积中累积的总剂量和剂量的均方根偏差(RMS)。这些信息提供了模拟的一个总结和物理结果的量化。

6.AddEdep 成员函数

void RunAction::AddEdep(G4double edep)
{
  fEdep  += edep;
  fEdep2 += edep*edep;
}

在这个函数中:

  • fEdep 是一个累积变量,它在整个模拟运行期间跟踪记录能量沉积的总和。
  • fEdep2 是另一个累积变量,用于跟踪记录能量沉积平方的总和。
    当这个函数被调用时,它接收一个 edep 参数,这是在当前事件中记录的能量沉积量。

fEdep += edep; 这一行将传入的 edep 值加到 fEdep 变量上。这是一个累加操作,用于更新总能量沉积。
fEdep2 += edep*edep; 这一行首先计算 edep 的平方,然后将结果加到 fEdep2 变量上。这同样是累加操作,但它跟踪的是能量沉积的平方,这在统计分析中非常有用,尤其是在计算均方根(RMS)或方差时。

通过这种方式,RunAction 类可以在模拟的每个事件中更新能量沉积的统计数据,然后在运行结束时,这些数据可以用来计算平均能量沉积及其统计分布。这对于理解模拟结果和进行科学分析是至关重要的。

6.SteppingAction.cc

#include "SteppingAction.hh"
#include "EventAction.hh"
#include "DetectorConstruction.hh"

#include "G4Step.hh"
#include "G4Event.hh"
#include "G4RunManager.hh"
#include "G4LogicalVolume.hh"

namespace B1
{

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

SteppingAction::SteppingAction(EventAction* eventAction)
: fEventAction(eventAction)
{}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void SteppingAction::UserSteppingAction(const G4Step* step)
{
  if (!fScoringVolume) {
    const auto detConstruction = static_cast<const DetectorConstruction*>(
      G4RunManager::GetRunManager()->GetUserDetectorConstruction());
    fScoringVolume = detConstruction->GetScoringVolume();
  }

  // get volume of the current step
  G4LogicalVolume* volume
    = step->GetPreStepPoint()->GetTouchableHandle()
      ->GetVolume()->GetLogicalVolume();

  // check if we are in scoring volume
  if (volume != fScoringVolume) return;

  // collect energy deposited in this step
  G4double edepStep = step->GetTotalEnergyDeposit();
  fEventAction->AddEdep(edepStep);
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

}

7.exampleB1.cc

#include "DetectorConstruction.hh"
#include "ActionInitialization.hh"

#include "G4RunManagerFactory.hh"
#include "G4SteppingVerbose.hh"
#include "G4UImanager.hh"
#include "QBBC.hh"

#include "G4VisExecutive.hh"
#include "G4UIExecutive.hh"

#include "Randomize.hh"

using namespace B1;

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

int main(int argc,char** argv)
{
  // Detect interactive mode (if no arguments) and define UI session
  //
  G4UIExecutive* ui = nullptr;
  if ( argc == 1 ) { ui = new G4UIExecutive(argc, argv); }

  // Optionally: choose a different Random engine...
  // G4Random::setTheEngine(new CLHEP::MTwistEngine);

  //use G4SteppingVerboseWithUnits
  G4int precision = 4;
  G4SteppingVerbose::UseBestUnit(precision);

  // Construct the default run manager
  //
  auto* runManager =
    G4RunManagerFactory::CreateRunManager(G4RunManagerType::Default);

  // Set mandatory initialization classes
  //
  // Detector construction
  runManager->SetUserInitialization(new DetectorConstruction());

  // Physics list
  G4VModularPhysicsList* physicsList = new QBBC;
  physicsList->SetVerboseLevel(1);
  runManager->SetUserInitialization(physicsList);

  // User action initialization
  runManager->SetUserInitialization(new ActionInitialization());

  // Initialize visualization
  //
  G4VisManager* visManager = new G4VisExecutive;
  // G4VisExecutive can take a verbosity argument - see /vis/verbose guidance.
  // G4VisManager* visManager = new G4VisExecutive("Quiet");
  visManager->Initialize();

  // Get the pointer to the User Interface manager
  G4UImanager* UImanager = G4UImanager::GetUIpointer();

  // Process macro or start UI session
  //
  if ( ! ui ) {
    // batch mode
    G4String command = "/control/execute ";
    G4String fileName = argv[1];
    UImanager->ApplyCommand(command+fileName);
  }
  else {
    // interactive mode
    UImanager->ApplyCommand("/control/execute init_vis.mac");
    ui->SessionStart();
    delete ui;
  }

  // Job termination
  // Free the store: user actions, physics_list and detector_description are
  // owned and deleted by the run manager, so they should not be deleted
  // in the main() program !

  delete visManager;
  delete runManager;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo.....
  • 11
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值