一个菜鸟的Geant4入门之路:α\alphaα粒子轰击金箔的例子

文章目录

  • 一个菜鸟的Geant4入门之路:α\alphaα粒子轰击金箔的例子
    • 前言
    • 去哪里找资料:
    • 几个重要的类
      • 一个活的G4程序需要哪几个类
        • runManager
        • DetectorConstruction、PhysicsList和PrimaryGenerator
      • 一个“不平庸”的G4程序额外需要的类
        • visManager
        • Optional User Actions
      • 小结
    • 开始写例子
      • T1programe物理情景概述
      • runManager & main()函数
      • T1DetectorConstruction
        • Soild:
        • Logical Volume & Material
        • Physical Volume
      • T1PrimaryGeneratorAction
      • T1ActionInitialization
      • Three UserActions & ROOT
        • G4AnalysisManager——管理Histogram
        • 收集数据
      • 其他
        • PhysicsList
        • 宏命令
        • CMakeLists.txt
    • 最后

前言

一切都要从一只蝙蝠说起
这篇博客是我初学Geant4,第一次写下一个可以成功运行,且能将一些及其简单的数据记录成ROOT文件的程序之后,写下的。恰逢寒假和新冠病毒严峻的疫情,我有了足够的空闲时间,可以比较仔细地记录一下。我没有通读官方文档,只是这看看那看看,然后直接上手做了,所以我想必然有很多错误,留给之后慢慢来。就作为消遣,以及作为经历分享一下。

去哪里找资料:

官方的文档应该是最好的教程了,但是一开始的时候总是会在里面迷路,加上英语也不好,就不知道每个板块在讲什么,遇到问题不知道怎么查找。我觉得这可能一开始都有的问题,首先介绍一下怎么用官网上的内容。

这是Geant4的OverView界面:


右上角的Download是下载软件的地方。最下面的Events包含世界各地开的类似暑期学校的活动,其中一些活动会把PPT之类的资源放在TimeTable上。例如其中XVI Seminar on Software for Nuclear, Subnuclear and Applied Physics:

最常用的是UserSupport那个模块,进去之后:

最常用的是8.c. ApplicationGuide,它结合examples讲解各个功能的用法。一想到要通读这么一本书哪怕是其中的一章,总会觉得有些急躁,所以我觉得可以从example看起,安装Geant4之后,在其路径下share/example/里有很多写好的例子,从B1开始内容逐渐丰富。比B1还简单的可能是ApplicationGuide中的第一章。顺着这些例子,哪里不会就去ApplicationGuide中搜索,到词条出现的位置,然后读那些相关的内容。

**其实Geant4作为一个如此庞大的框架,其中的每一个类、每一个方法的命名都是非常直白的,**比如G4DetectorConstruction,一看就是用来搭探测器的。还有很多宏命令,比如/run/beamOn也很容易理解,所以它的examples,甚至是G4的头文件,都没那么可怕。上图中的3. Source code放的就是G4的各种类和其中的方法,3.c. doxygen documentation是一个很好用的页面,学ROOT的时候,就有类似的页面,可以查找各种类和它们的方法,直接告诉你它们怎么用。在看过几个examples之后,这个页面就成了我最常用的资源了。(P.S. 有的时候网络质量比较糟糕,作为替代,我其实更多的是去G4的include目录下直接翻头文件这样。)

上图是一个简单的说明,在doxygen documentation中随便搜索一个类,可以看到它的各种方法。当你想做的事情在example中没有直接的代码可以抄的时候,可以翻这个。

到目前为止,我觉得学习Geant4的最舒服的方法是看例子,里面每一行命令之间相互联系大多不是很强,以至于给人一种命令行语言一样的感觉。所以没什么好怕的(说这句话的原因是我刚开始学的时候看到那么多文件和英文文档人都要吓傻了233)

几个重要的类

首先,Geant4是基于C++编写的面向对象的框架。我没有系统的学过C++和面向对象,在我的理解上,Geant4就像是一个大的车间,里面有各种各样的机床、工具箱和原材料,要让一个程序工作,你不需要知道机床和工具的内部构造是什么,你只需要写一张待办事项表,告诉工人“用一号工具箱里的锯子把台虎钳上夹着的木材锯成爱的形状”。

你用到的工具的种类总是根据你的工程难度决定的,做一架歼20一定比做一根筷子复杂的多。但作为一个初学者只希望工人们不会手忙脚乱把车间搞的一团糟,那最简单的就是仅仅使用必要的几个工具,它们是一个程序中最核心的几个类。

(P.S. 光做比喻是远远不够的,开始之前,我们需要去熟悉一下面向对象的常识,我觉得菜鸟教程的内容很适合快速入门,知道了基本规则之后,随着实践会遇到很多奇怪的问题,这个时候针对性地翻看C++ Primer可以补充一些有用的知识。)

一个活的G4程序需要哪几个类

runManager

runManager是类G4RunManager的实例化对象(出于可读性的考虑,G4以及大部分程序的类名和对象名都很像,为了方便,我习惯用一般的对象名称呼,因为这样可以省去前面的“G4”),可以说是整个工程的包工头、程序的躯干。它负责整个程序最顶层的控制。从ApplicationGuide 2.1.1的main函数中可以获得一定的了解:

int main()
{// construct the default run managerG4RunManager* runManager = new G4RunManager;// set mandatory initialization classesrunManager->SetUserInitialization(new ExG4DetectorConstruction01);runManager->SetUserInitialization(new ExG4PhysicsList00);runManager->SetUserInitialization(new ExG4ActionInitialization01);// initialize G4 kernelrunManager->Initialize();// get the pointer to the UI manager and set verbositiesG4UImanager* UI = G4UImanager::GetUIpointer(); UI->ApplyCommand("/run/verbose 1"); UI->ApplyCommand("/event/verbose 1"); UI->ApplyCommand("/tracking/verbose 1");// start a runint numberOfEvent = 3; runManager->BeamOn(numberOfEvent);// job terminationdelete runManager;return 0;
}

从代码本身就可以很轻松的读出,runManager从被实例化之后,先“搭建”了探测器的结构,规定了模拟将会包含的物理过程、规定了用户行为,之后启动G4内核,运行模拟,最后结束模拟。runManager有一个特点很便捷,如ApplicationGuide 2.1.2所讲:

When G4RunManager is created, the other major manager classes are also created. They are deleted automatically when G4RunManager is deleted.

也就是说,我们不需要花心思把整个程序中创造的所有对象指针都记在小本本上,然后设计在哪里删除它们。只有少量的几个对象需要特别删除,之后会特别提醒。其实很好理解,就是很多东西太重要了,任何一个模拟程序中都应该有他们,所以G4就直接帮你删掉他们,而一些不是很重要,没有也不会有硬性错误的对象,G4不能预料到他们是否存在,也就需要显式地delete掉他们。

DetectorConstruction、PhysicsList和PrimaryGenerator

从上面的例子大概可以猜到,对于一个模拟来说,除了runManager之外,应该要有一个探测器结构(DetectorConstruction),告诉内核它将虚拟一个什么样的物理环境;一个物理列表(PhysicsList),告诉内核它要考虑哪些物理过程,这规定了一个范围,一个卢瑟福散射实验只需要考虑涉及的电磁过程而不需要让强相互作用方面的计算占用计算资源;以及,还需要一个初始事件(PrimaryGenerator),让这个故事有一个开始的地方。

前两个由runManager->SetUserInitialization()统一管理,而PrimaryGenerator可以由runManager->SetUserAction()设置,也可以间接地由UserActionInitialization->SetUserAction()来管理(P.S. [他和其他的一些类一起被打包在UserActionInitialization中](#Optional User Actions))。

一个“不平庸”的G4程序额外需要的类

以上几个部分都齐全之后,这个程序按理说就可以运行了,但停留在这样的状态并不能发挥一个模拟软件的功能——所有的舞台都搭起来了,演员们都准备就绪,但幕布没有拉开,话筒也是关闭的,甚至连解说员也没有,一台戏唱完,用户什么也得不到。

作为一个有用的程序,还需要一些辅助型选手,把内核中发生的事情告诉我们。

visManager

visManager是管visualization的,它和runManager级别类似,都在main函数中构造出来,在main函数结束时被析构掉(可以看到,UImanager同样也是在main函数中构造并使用的,但它并不需要显式地delete)。

int main()
{
...// Initialize visualization//G4VisManager* visManager = new G4VisExecutive;// InitializevisManager->Initialize();
...delete visManager;return 0;
}

简单讲,有了这个类,就可以看到B1中那个可视化界面了。通过这个界面你可以大致检查一下模拟程序是否符合你心中的构想,从而确认程序有没有内容上的差错。但我觉得对于收集数据来说意义不大,所以我在学习中跳过了这一节,在自己的程序中把B1的相应内容硬搬过来,不影响其他环节,之后需要可以补上。

Optional User Actions

如上文所说,可视化界面只能给用户一个直观的、定性的印象,但我们总是需要从模拟中获取确切的数据。例如,粒子在物质中的能量沉积、动量变化以及轨迹信息等。这些信息往往是从模拟中的每一小步产生的,所以需要在模拟的每一小步上做一些记录的操作。

这要涉及到Geant4模拟中的一些逻辑概念。想象一个高能物理实验,机器一开,直到停止,叫做一次“Run”,机器中产生一个初始粒子,直到这个粒子以及它的次级粒子消失不见,叫做一个“Event”,在这其中,每个粒子的一生都被划分成很多小步来计算,叫做“Step”。(这个概念在各种博客和PPT中都被反复提及,就不盗图了。)

那么相应的,就有runAction、eventAction和SteppingAction等。

他们和PrimaryGenerator一起在UserActionInitialization中被打包起来交给runManager:

void main()
{...runManager->SetUserInitializaition(new T1ActionInitializaiton(detConstruction));...
}
void T1ActionInitialization::Build()
{SetUserAction(new T1PrimaryGeneratorAction);SetUserAction(new T1RunAction);auto eventAction = new T1EventAction(); SetUserAction(eventAction);SetUserAction(new T1SteppingAction(eventAction));
}

小结

总结一下各个类之间的关系:

G4VUserActionInitialization
G4VUserPrimaryGeneratorAction
G4UserRunAction
G4VEventAction
G4VSteppingAction
...
G4RunManager
G4VUserDetectorConstruction
G4VUserPhysicsList
G4VisManager

开始写例子

源码链接

这部分的主要内容是每个类中需要完成的工作,在ApplicationGuide中已经结合example对每个类要做的事情有了详细的讲解了,我尽可能地写清楚我是怎么找到他们的。

在这之前想提一下C++上的事情,对于一个类,要有声明要有定义,我听说有很多不同的写法,但我觉得比较规范的还是像example那样,把一个类的声明放在一个.hh文件里,定义放在.cc文件里,然后把.hh放在include文件夹里,.cc放在src文件夹里,总的main函数放在和这两个文件夹同样层级的目录里。

下面是G4自带的exampleB1的目录结构:

/usr/local/share/Geant4-10.5.1/examples/basic/B1
├── CMakeLists.txt
├── GNUmakefile
├── History
├── README
├── exampleB1.cc
├── exampleB1.in   # 运行时的命令行
├── exampleB1.out  # exampleB1建议把程序输出重定向到这个文件里,实际上不重要
├── include
│   ├── B1ActionInitialization.hh
│   ├── B1DetectorConstruction.hh
│   ├── B1EventAction.hh
│   ├── B1PrimaryGeneratorAction.hh
│   ├── B1RunAction.hh
│   └── B1SteppingAction.hh
├── init_vis.mac
├── run1.mac
├── run2.mac
├── src
│   ├── B1ActionInitialization.cc
│   ├── B1DetectorConstruction.cc
│   ├── B1EventAction.cc
│   ├── B1PrimaryGeneratorAction.cc
│   ├── B1RunAction.cc
│   └── B1SteppingAction.cc
└── vis.mac  # 有关可视化的一些设置脚本,使用G4的宏命令

头文件的写法,服从C++的语法就可以,需要注意的是,头文件总是没办法避免在各种不同的脚本中声明,这样当一整个项目串起来编译的时候,就会出现重复引用的情况,所以需要在每一个头文件开头使用这种宏:

#ifndef ClassName_h
#define ClassName_h 1... // 内容#endif

T1programe物理情景概述

就像exampleB1那样,我给这个小例子起名为T1programe,其中包含的各种类,我都在它的命名前加一个T1

如前面介绍的那样,这个例子的想法是模拟一下卢瑟福散射的过程,也就是α\alphaα粒子轰击金箔。边长10cm的正方形金箔,厚度设定为一个金原子直径288pm,α\alphaα粒子从距离2.5cm的地方垂直射向靶心。

runManager & main()函数

runManager直接从G4RunManager实例化,并不需要额外继承一个类出来。需要写的是main()函数。这部分和exampleB1.cc中的内容非常相似。

首先,我们希望程序即可以实现可视化界面模式运行,也可以在batch(批处理)模式下运行,这决定于我们运行程序时是否在后面追加了批处理文件:

./T1program #可视化交互模式
./T1program run1.mac #批处理模式,程序读取批处理文件,并按照其中的宏命令运行

关于运行模式,详见ApplicationGuide 2.10,其中有’Hard-coded’ Batch Mode、Batch Mode with Macro File、Interactive Mode Driven by Command Lines三种模式。在2.10.5中,介绍了同时具备两种模式运行功能的方法。

程序识别调用时是否追加批处理文本作为参数,以此判断启用何种运行模式:

int main(int argc, char** argv){G4UIExecutive* ui = 0;if ( argc == 1 ){ui = new G4UIExecutive(argc, argv);}// ......

简单讲,形参argcargv分别代表一条运行程序的命令行,其中包括的“单词”的个数和内容,上面两条命令,argc分别为1和2,argv的内容分别是["./T1program"]和["./T1program", “run1.mac”](这里为了便捷,忽略了argv是指针的指针,这个细节可以在网上轻易地查到)。

接下来根据ui是否是一个空指针,来配置不同的运行模式:

    // interactive modeif ( ! ui ) {// batch modeG4String command = "/control/execute ";G4String fileName = argv[1];UImanager->ApplyCommand(command+fileName);}else {// interactive modeUImanager->ApplyCommand("/control/execute init_vis.mac");ui->SessionStart();delete ui;}

其他的内容就基本上和ApplicationGuide 2.1.1中main函数差不多了,runManager自从实例化出来之后,要做的就是设置好相应的DetectorConstruction等类,最后delete掉,visManager只需要调用Initialization方法,最后delete掉。

T1DetectorConstruction

ApplicationGuide 2.2 & 2.3中,分别讲解了如何去定义一个探测器结构以及定义一个特定的材料。T1DetectorConstruction包含的内容十分简单,应该没有超出这两章的范围。除此之外,探测器还可以实现诸如外加电磁场、计数等功能,更详细的内容大概在*ApplicationGuide, Chapter 4.*中。

与其说是探测器,不如直接说是物体。模拟中可能涉及到很多物体,其中最大的物体是整个世界,它可以是一个大水缸,可以是一坨球形的空气,无论如何,它总要有,而且要容纳得下其他的所有物体。

所有的物体,就像建造它们一样,首先要有图纸描绘它们的形状,再用特定的材料将它们制作出来,最后再把它们放在正确的位置上。因此也就有了三个概念,它们都各自对应一个类,分别叫做Solid、LogicalVolume、PhysicalVolume。

full properties
position
Soild: Shape
LogicalVolume
PhysicalVolume

以上的这些工作都放在T1DetectorConstruction::Construct()方法里:

Soild:

Chapter 4.1.2中列出了很多常用的类,本例子只用到了box:

G4Box(const G4String& pName,  G4double pX,  // half length in XG4double pY,  // half length in YG4double pZ)  // half length in Z

基于这一条,我定义了T1中唯二的Soild:

// World
G4double world_sizeXYZ = 1.2*pla_sizeYZ;G4Box* solidWorld =new G4Box("World",0.5*world_sizeXYZ, 0.5*world_sizeXYZ,0.5*world_sizeXYZ);
// Plate parameters
G4double pla_sizeX = 288e-12*m, pla_sizeYZ = 10*cm;G4Box* solidPla = new G4Box("Plate",0.5*pla_sizeX,0.5*pla_sizeYZ,0.5*pla_sizeYZ);

G4的各种参数都需要带单位,G4也自带了一套单位系统可以直接使用,它们是在$CLHEP_BASE_DIR/include/CLHEP/Units/SystemOfUnits.h定义的(我没有搞清楚CLHEP和G4的关系,但从我安装的情况看,CLHEP这个文件夹存在于G4的include目录下。在G4的include目录中,G4SystemOfUnits.hh打包了一些常用的单位。

关于单位系统的说明在Chapter 3.3中。

Logical Volume & Material

ApplicationGuide 2.3中解释了如何定义需要的材料,并且在ApplicationGuide 4.2.3中给出了很多例子。

G4有一个类G4NistManager,其中有很多现成的材料可以用,如ApplicationGuide:Listing2.10

G4NistManager* man = G4NistManager::Instance();G4Material* H2O = man->FindOrBuildMaterial("G4_WATER"); G4Material* Air = man->FindOrBuildMaterial("G4_AIR");

我从里面直接找出了Au,构造出金箔的LogicalVolume:

G4NistManager* nist = G4NistManager::Instance();G4Material* pla_mat = nist->FindOrBuildMaterial("G4_Au");
G4LogicalVolume* logicPla = new G4LogicalVolume(solidPla,            //its solidpla_mat,             //its material"Plate");            //its name

真空的定义不那么直接,按说它可以用极其稀薄的气体代替,但我试了几次没成功。最终我在ApplicationGuide中直接搜索了“Vacuum”这个词,它第一次出现就是在上面4.2.3中:

// What about vacuum? Vacuum is an ordinary gas with very low density
density = universe_mean_density;  //from PhysicalConstants.h
pressure = 1.e-19*pascal;
temperature = 0.1*kelvin;
new G4Material(name="Galactic", z=1., a=1.01*g/mole, density, kStateGas, temperature, pressure);

为了方便我从include/CLHEP/Units/PhysicalConstants.h中找到了上面的universe_mean_density = 1.e-25*g/cm3;

G4Material* world_mat =new G4Material("Galactic", 1., 1.01*g/mole,1.e-25*g/cm3, kStateGas,0.1*kelvin, 1.e-19*pascal);G4LogicalVolume* logicWorld =new G4LogicalVolume(solidWorld,          //its solidworld_mat,           //its material"World");            //its name

(其实是因为我知道怎么让编译器去找PhysicalConstants.h,直接引用它说引用太深)。

Physical Volume

G4VPhysicalVolume* physWorld =new G4PVPlacement(0,    //no rotationG4ThreeVector(),       //at (0,0,0)logicWorld,            //its logical volume"World",               //its name0,                     //its mother  volumefalse,                 //no boolean operation0);                    //copy number

其中mother volume是指容纳这个Volume的Volume,World是唯一一个没有mother volume的Volume,因为它是最大的。ApplicationGuide 2.2.6

An exception exists to the rule that a physical volume must be placed inside a mother volume. That exception is for the World volume, which is the largest volume created, and which contains all other volumes. This volume obviously cannot be contained in any other. Instead, it must be created as a G4PVPlacement with a null mother pointer. It also must be unrotated, and it must be placed at the origin of the global coordinate system.

Generally, it is best to choose a simple solid as the World volume, the G4Box solid type is used in all basic examples.

而金箔就需要传入一个LogicalVolume的指针,设定它的“母空间”:

G4VPhysicalVolume* physPla =new G4PVPlacement(0,                     //no rotationG4ThreeVector(),       //at (0,0,0)logicPla,              //its logical volume"Plate",               //its namelogicWorld,            //its mother volumefalse,                 //no boolean operation0);                    //copy number

关于转动的问题,ApplicationGuide 2.2.7中指引读者去看exampleB3

// place crystals within a ring
//
for (G4int icrys = 0; icrys < nb_cryst ; icrys++) {G4double phi = icrys*dPhi;G4RotationMatrix rotm  = G4RotationMatrix();rotm.rotateY(90*deg);rotm.rotateZ(phi);G4ThreeVector uz = G4ThreeVector(std::cos(phi),  std::sin(phi),0.);G4ThreeVector position = (ring_R1+0.5*cryst_dZ)*uz;G4Transform3D transform = G4Transform3D(rotm,position);new G4PVPlacement(transform,             //rotation,positionlogicCryst,            //its logical volume"crystal",             //its namelogicRing,             //its mother  volumefalse,                 //no boolean operationicrys,                 //copy numberfCheckOverlaps);       // checking overlaps
}

(这里有个问题,从这里看好像很多个PhysicalVoluem可以有同样的name?在ROOT中,一些类的构造函数中“name”是当作指针用的,就导致有的类可以共用name有的不能共用,不知道这里是不是类似的情况)

T1PrimaryGeneratorAction

因为T1只涉及一个简单的初始事件,可以直接类比exampleB1完成。

ApplicationGuide 2.7有很多详细有用的信息,例如一些宏指令。

类G4ParticleGun用在比较简单的场景下,入射一定数量的粒子,可以设置一些基本初始参数为常数或者随机数:

The following methods are provided by G4ParticleGun, and all of them can be invoked from the

generatePrimaries() method in your concrete G4VUserPrimaryGeneratorAction class.

  • void SetParticleDefinition(G4ParticleDefinition*)

  • void SetParticleMomentum(G4ParticleMomentum)

  • void SetParticleMomentumDirection(G4ThreeVector)

  • void SetParticleEnergy(G4double)

  • void SetParticleTime(G4double)

  • void SetParticlePosition(G4ThreeVector)

  • void SetParticlePolarization(G4ThreeVector)

  • void SetNumberOfParticles(G4int)

还有一个叫做G4GeneralParticleSource的类,可以说是“G4ParticleGun Pro”吧。

ApplicationGuide里面有很多令人头大的指导,我直接去翻了例子,然后稍微修修改改就成了。

在构造函数里面设置粒子种类和动量:

T1PrimaryGeneratorAction::T1PrimaryGeneratorAction()
: G4VUserPrimaryGeneratorAction(),fParticleGun(0),   // 这里使用初始化列表来初始化成员变量,相当于在函数内执行赋值fPlateDistanceX(2.5*cm)
{G4int n_particle = 1;fParticleGun  = new G4ParticleGun(n_particle); // G4ParticleGun的构造函数要传入一个整数作为粒子数,并且注意它应该在析构函数中被删除// default particle kinematicG4ParticleTable* particleTable = G4ParticleTable::GetParticleTable();G4String particleName;G4ParticleDefinition* particle= particleTable->FindParticle(particleName="alpha");// default particle momentumfParticleGun->SetParticleDefinition(particle);fParticleGun->SetParticleMomentum(G4ThreeVector(7.7*MeV,0.,0.));
}

然后在GeneratePrimaries方法中设置其他信息:

void T1PrimaryGeneratorAction::GeneratePrimaries(G4Event* anEvent)
{//this function is called at the begining of ecah event//G4double SourceCenterX = -fPlateDistanceX; G4double SourceCenterYZ = 0*cm;G4double RayWidth = 0.01*cm;    // 粒子的初始位置是一个正方形,边长是RayWidthG4double x0 = SourceCenterX + 0;G4double y0 = RayWidth * (G4UniformRand()-0.5) + SourceCenterYZ;G4double z0 = RayWidth * (G4UniformRand()-0.5) + SourceCenterYZ;fParticleGun->SetParticlePosition(G4ThreeVector(x0,y0,z0));fParticleGun->GeneratePrimaryVertex(anEvent);
}

这样的做法是可行的,但是我也不确定是不是唯一可行的。

T1ActionInitialization

这个类我觉得就是个清单,就疯狂SetUserAction这样就完了,

void T1ActionInitialization::Build() const
{SetUserAction(new T1PrimaryGeneratorAction);SetUserAction(new T1RunAction);auto eventAction = new T1EventAction();SetUserAction(eventAction);SetUserAction(new T1SteppingAction(eventAction));
}

Three UserActions & ROOT

最后一块是用来获取数据的,我是看了ApplicationGuide Chapter9的部分内容做的,其中9.2.1开始提到了exampleB4,例子里写的还是挺明白的。

因为就是一次简单的尝试,所以就做的比较简单,从模拟中提取出射α\alphaα粒子的动量x分量,然后把它录入到root的histogram中。

在我的理解上,他们三个在时间上依次包含,一个Run里包含多个Event,Event又包含很多Step,而他们相应的Action,就是规定每个Run、Event、Step开始和结尾要做什么。这个就和普通的面向对象编程差不多了,想干什么干什么。

[我看这个时序图还挺好看的,就随便画了一个]

G4AnalysisManager——管理Histogram

将数据写成ROOT文件,需要使用g4root类:

//file: T1Analysis.hh
#ifndef T1Analysis_h
#define T1Analysis_h 1#include "g4root.hh"#endif
//file: T1RunAction.cc#include "T1Analysis.hh"
...

我猜这样就会让电脑知道未来要创建的是ROOT文件而不是excel什么的了。

在RunAction的构造函数中,要初始化一个G4AnalysisManager的指针,这个对象时用来管理模拟过程中用到的直方图等内容的:

auto analysisManager = G4AnalysisManager::Instance();

Instance()方法在第一次调用时创建一个G4AnalysisManger指针,当这个指针已经存在时,它将返回这个指针。在EventAction中,要用到这个对象,将数据填充到创建的直方图里,所以也要调用Instance()方法,从而拿到这个指针。

analysisManager可以创建ROOT的数据类型,例如histogram.

analysisManager->CreateH1("p_x",  // Name"X component of momentum",  // Describe70,         // Number of bins/*-5*cm*/-7.7*MeV,/*5*cm*/  7.0*MeV);    // Range of the histogram

创建后可以填充数据:

analysisManger->FillH1(0,fPx);

其中0是序号,如果analysisManger先后创建了多个histogram,就依次从0往后编号。

还可以读写文件:

analysisManager->OpenFile("T1_Px");
analysisManager->Write();
analysisManager->CloseFile();

要注意在RunAction的析构函数中记得delete analysisManager

收集数据

SteppingAction里只放一个UserSteppingAction()方法,用来收集数据。这个方法传入G4Step指针:


void T1SteppingAction::UserSteppingAction(const G4Step* step)

通过它可以获得关于Step的很多信息。例如获得步长:

G4double steplength = 0.;
steplength = step->GetStepLength();

通过GetPreStepPoint()GetPostStepPoint()可以获得分别代表这一步起点和终点的指针,进而获得动量和位置等:

// Get Pz
G4ThreeVector P = step->GetPostStepPoint()->GetMomentum();
G4double Px = P.x();
G4cout << "      X = " << step->GetPostStepPoint()->GetPosition().x();

其他

PhysicsList

已经有一些可以直接拿来用的Physicslist,在[去哪里找资料]那个用户支持页面里的8.f Physics List Guide中有说明。我这就随便填了一个默认的: FTFP_BERT

宏命令

在[[runManager & main()函数]](#runManager & main()函数)中,提到了导入宏文件:

UImanager->ApplyCommand(command+fileName);

其实就是一堆命令组成的批处理文件,每一条命令都可以单独起作用,在可视化界面中也可以使用。最常用的可能是/run/beamOn n吧,大致是指放出几波粒子。(我见有人提过,beamOn十次,每次释放一个粒子,和beamOn一次,释放十个粒子是否有区别?我也没试过,但可以试一下。)

另外,main()中提到的vis_init.mac文件是用来在可视化界面中“置办家具”的,把构建好的想看到的东西都可视化出来,犹豫我的DetectorConstruction和PrimaryGeneratorAction和exampleB1很相似,我就直接拷贝过来用了,并没有什么大问题,里面的内容一眼看上去也比较浅显易模仿。我还没有仔细看(嘿~)。

CMakeLists.txt

这个文件是编译的时候告诉电脑一个项目有哪些文件、依赖什么环境用的,不属于G4的范畴了。翻看example的CMakeLists,大概能知道哪些位置要填什么。

到此为止,这个小项目就算完成了,在可视化界面,以及输出的Histogram中都没有太大鬼畜的地方,一片安静祥和。

其实可以输出角度的,不过没想那么多。

最后

莫名其妙就写了6000多字,想必废话挺多的哈哈哈。终于写完了,算是第一次认真写博客(其实写到后来感觉自己越来越菜就像是在抄书,最后写完也懒得改臭不要脸地就决定发了。)。平时着急,问题解决之后,总是会觉得好像就那么回事,曾经卡住的地方也没什么好写的,回头看的时候总是看不懂自己写的什么。

过几天就开学了,以此纪念这个寒假。

一个菜鸟的Geant4入门之路:alpha粒子轰击金箔的例子相关推荐

  1. 一个VC爱好者的入门之路

    看到那些对VC不知从何下手而苦苦挣扎的朋友,希望我的学习之路能给他们一下借鉴.学VC并不是传说的那么难,可不下些功夫是学不成的.学编程急不得,没有编程的基础知识上来就学VC肯定碰一头灰,说VC难就难在 ...

  2. 是非人生 — 一个菜鸟程序员的5年职场路 第7节

    是非人生 - 一个菜鸟程序员的5年职场路第7节作者: 花8 天涯IT: http://cache.tianya.cn/publicforum/content/itinfo/1/77229.shtml ...

  3. 是非人生 — 一个菜鸟程序员的5年职场路 第8节

    是非人生 - 一个菜鸟程序员的5年职场路第8节作者: 花8 天涯IT: http://cache.tianya.cn/publicforum/content/itinfo/1/77229.shtml ...

  4. 是非人生 — 一个菜鸟程序员的5年职场路 第6节

    是非人生 - 一个菜鸟程序员的5年职场路第6节作者: 花8 天涯IT: http://cache.tianya.cn/publicforum/content/itinfo/1/77229.shtml ...

  5. 是非人生 — 一个菜鸟程序员的5年职场路 第14节

    是非人生 - 一个菜鸟程序员的5年职场路第14节作者: 花8 天涯IT: http://cache.tianya.cn/publicforum/content/itinfo/1/77229.shtml ...

  6. 是非人生 — 一个菜鸟程序员的5年职场路 第4节

    是非人生 - 一个菜鸟程序员的5年职场路第4节作者: 花8 天涯IT: http://cache.tianya.cn/publicforum/content/itinfo/1/77229.shtml ...

  7. 是非人生 — 一个菜鸟程序员的5年职场路 第1节

    是非人生 - 一个菜鸟程序员的5年职场路第1节作者: 花8 天涯IT: http://cache.tianya.cn/publicforum/content/itinfo/1/77229.shtml ...

  8. 是非人生 — 一个菜鸟程序员的5年职场路 第9节

    是非人生 - 一个菜鸟程序员的5年职场路第9节作者: 花8 天涯IT: http://cache.tianya.cn/publicforum/content/itinfo/1/77229.shtml ...

  9. 是非人生 — 一个菜鸟程序员的5年职场路 第28节

    是非人生 - 一个菜鸟程序员的5年职场路第28节作者: 花8 天涯IT: http://cache.tianya.cn/publicforum/content/itinfo/1/77229.shtml ...

最新文章

  1. 微信小程序实时聊天之WebSocket
  2. find、sed、awk、grep命令总结
  3. redis 和 memcached 有什么区别?redis 的线程模型是什么?为什么 redis 单线程却能支撑高并发?...
  4. 自写网站阶段之:终结篇
  5. 在react里写原生js_小程序原生开发与第三方框架选择
  6. codevs 2185 最长公共上升子序列--nm的一维求法
  7. 加入 Git 版本管理(git的基本使用)
  8. 手把手教你强化学习 (七) 强化学习中的无模型控制
  9. 「leetcode」763.划分字母区间【贪心算法】详细图解
  10. 在SQL2008中,如何让id自动生成并自动递增?如何让时间默认生成?
  11. android html5小游戏源代码下载,HTML5小游戏源代码大全
  12. 2020年最全Python常用爬虫代码就这些了(附爬虫教程)
  13. Java实现数组反转
  14. Renascence使用方法
  15. 婴儿级教学,手伤害教你用鸿蒙OS获取B站粉丝数!!
  16. Json汉化-使用JavaScript和百度翻译API免费实现Json文件的汉化
  17. 抽奖活动前端源码-可录入抽奖人名单
  18. 关于web前端大作业的HTML网页设计——我的班级网页HTML+CSS+JavaScript
  19. 2401C可解RFX2401C缺货的燃眉之急,也可直接诶替代CC2592/CC2591
  20. 身份证如何扫描成电子版?这个方法很好用

热门文章

  1. 打破校史!「双非」高校,首发 Science
  2. Qt 快速入门学习笔记
  3. 如何使用easywechat开发微信支付功能
  4. 物联网卡企业的选择应该怎么避雷
  5. 《炬丰科技-半导体工艺》硅片湿法清洗技术
  6. 花书 第十九章 第二十章
  7. linux加密压缩文件gpg,linux文件--GPG 加密解密
  8. 《大数据之路:阿里巴巴大数据实践》-第3篇 数据管理篇 -第15章 数据质量
  9. Python基础语法——基础语法、变量
  10. 论文写作各种图片格式转成eps格式(inkscape)