Geant4中Messenger类的创建与使用


如何创建、使用自定义的Messenger类?

在C++中,我们可以把一个**类的声明**写进与类名相同的头文件中(className.hh),把**定义**写进cxx文件(className.cxx)中。类的声明格式为

class MyClass : public MotherClass{public:......private:....};

注意:

结尾有一个分号头文件的书写规范,参考这篇文章:http://www.cplusplus.com/forum/articles/10627/精华部分:do nothing if: A makes no references at all to Bdo nothing if: The only reference to B is in a friend declarationforward declare B if: A contains a B pointer or reference: B* myb;forward declare B if: one or more functions has a B object/pointer/reference as a parementer, or as a return type: B MyFunction(B myb);#include "b.h" if: B is a parent class of A#include "b.h" if: A contains a B object: B myb;如和让geant4“认得”我们的类?如何在geant4中使用messenger类?geant4如何知道我们写了一个messenger类?geant4中Messenger类是靠G4UImanager类来管理的。这个类是一个singleton类。什么是singleton?在c++中使用singleton有什么好处?

geant4中使用了大量的singleton类。介绍见 http://en.wikipedia.org/wiki/Singleton_pattern singleton类拥有一个指向本类类型的静态(成员变量)指针,例如fMyClass。

什么是静态成员变量?

一个类的静态成员变量可以通过 MyClass::myStaticVar这样直接引用,它需要在MyClass.cxx文件中所有成员函数定义之外(相当于定义文件全局变量位置)处定义。注意既然是定义,就需要带上类型:

staticVarType MyClass::myStaticVar = myClassStaticInitVal;

这个类的所有对象公用这一个变量,可以通过object.myStaticVar访问该变量。singleton的这个指向本类类型的静态指针fMyClass初值必须赋为NULL。singleton类不允许直接pointer = new SingletonClass或者obj = SingletonClass(),其构造函数为private类型。singleton类会有一个public类型的静态成员方法(即可以通过MyClass::myStaticMethod()方式调用的方法),一般称为GetInstance()`等等。这个方法会检测fMyClass指针是否为空,如果为空则new MyClass。最后反悔fMyClass的值。采用singleton可以很好的保证大型程序中这个类只有一个实例,一般的Service、Manageer、Container类都是singleton的。所以在程序中,我们采用下面的程序来初始化UImanger这个singleton:0

G4UImanager* UI;UI = G4UImanager::GetUIpointer();

在GetUIpointer()中会做这两件事:

fUImanager = new G4UImanager; fUImanager->CreateMessenger();

G4UImanager的构造函数可以看到

64 G4UImanager::G4UImanager()65 : G4VStateDependent(true),66 UImessenger(0), UnitsMessenger(0)

这里G4VStateDependent类是G4UImanager的父类;它的构造函数会调用另一个singleton并且register这个对象(G4UImanager)是否是State相关。构造函数中出现了这一行

treeTop = new G4UIcommandTree("/")

这里G4UIcommandTree类有一个vector名叫tree,用来存放所有的command。

这个类不是singleton,但是因为只有G4UImanager这个singleton会创建G4UIcommandTree这个类,所以整个geant4程序中也只会有一个G4UIcommandTree对象。(推测)UImessenger = new G4UIcontrolMessenger; UnitsMessenger = new G4UnitsMessenger;

在主程序中,我们通过

UI->ApplyCommand("/control/execute run.mac");

这样的命令来运行某个mac文件在ApplyCommand方法中,它首先尝试理解命令字符串

G4String aCommand = SolveAlias(aCmd);

然后通过

G4UIcommand * targetCommand = treeTop->FindPath( commandString );

这句话根据字符串在treeTop(也就是G4UIcommandTree类)找到G4UIcommand对象targetCommand最后执行它:

return targetCommand->DoIt( commandParameter );

那么什么时候我们自己所写的messenger被放进了treeTop的tree这个vector呢?commandTree类有一个成员函数:

void AddNewCommand(G4UIcommand * newCommand);

搜索这个成员函数,在void G4UIcommand::G4UIcommandCommonConstructorCode中被调用过。原来创建一个G4UIcommand类的对象时,调用其构造函数G4UIcommand::G4UIcommand(const char * theCommandPath,...)时,它会调用G4UIcommandCommonConstructorCode (comStr); 这里G4String comStr = theCommandPath;并且被稍加处理。在void G4UIcommand::G4UIcommandCommonConstructorCode中

G4UImanager::GetUIpointer()->AddNewCommand(this);

而在G4UImanager中

void G4UImanager::AddNewCommand(G4UIcommand * newCommand) { treeTop->AddNewCommand( newCommand ); }

所以每一个G4UIcommand都是在创建时写入了G4UImanager的commandTree中。所以在利用G4UImanager调用一个命令时,我们需要先创建这个命令的G4UIcommand对象。

那么,我们是什么时候创建G4UIcommand对象的呢? ** 当我们 new xxxmessenger()时 **我们自己写的messenger类一定是G4UImessenger类的继承类。我们在messenger类中要做两件事:.

创建所有的command对象,要传入类似于"/control/execute"的字符串。

我再在xxxmessenger的构造函数中做这一件事情,例如:

factoryCmd = new G4UIcmdWithAString("/histo/fileName",this); factoryCmd->SetGuidance("set name for the histograms file"); factoryCmd->AvailableForStates(G4State_PreInit,G4State_Idle);

告诉geant4当你执行这个命令时你会进行什么操作。

G4UImanager在ApplyCommand时,最后操作归结到

return targetCommand->DoIt( commandParameter );

通过查看G4command中DoIt函数可以看到最后归结到这句话:

messenger->SetNewValue( this, correctParameters );

而messenger是这样初始化的:

G4UIcommand::G4UIcommand(const char * theCommandPath, G4UImessenger * theMessenger) :messenger(theMessenger),token(IDENTIFIER),paramERR(0)

所以messenger是通过G4UIcommand的构造函数中传入的。在上面的例子中就是this,而最后的工作归结到这个messenger类的SetNewValue成员函数。而在这个成员函数中我们为了实现一些可能需要调用一些Action类的成员函数。例如利用G4UImanager修改事例初级顶点。

利用G4UImanager修改事例初级顶点我们通过在main中 runManager->SetUserAction(new PrimaryGeneratorAction)来读入这个类。在读入的时候我们就为PrimaryGeneratorAction类的对象在内存中分配了空间,并对一些变量赋了(默认的)初值。为了能成功的修改初级顶点的位置,我们需要初级顶点是在什么时候被产生的。我们发现,runManager->Initialize()函数并没有产生初级顶点,在runManager->BeamOn的时候才开始产生顶点。BeamOn函数内容如下:

152 numberOfEventToBeProcessed = n_event;153 ConstructScoringWorlds();154 RunInitialization();155 if(n_event>0) DoEventLoop(n_event,macroFile,n_select);156 RunTermination();

而DoEventLoop如下:

232 InitializeEventLoop(n_event,macroFile,n_select); 233 234 // Event loop 235 for(G4int i_event=0; i_event<n_event; i_event++ ) 236 { 237 ProcessOneEvent(i_event); 238 TerminateOneEvent(); 239 if(runAborted) break; 240 } 241 242 TerminateEventLoop();

这里ProcessOneEvent()内容如下:

261 void G4RunManager::ProcessOneEvent(G4int i_event) 262 { 263 currentEvent = GenerateEvent(i_event); 264 eventManager->ProcessOneEvent(currentEvent); 265 AnalyzeEvent(currentEvent); 266 UpdateScoring(); 267 if(i_event<n_select_msg) G4UImanager::GetUIpointer()->ApplyCommand(msgText); 268 }

而GenerateEvent()包含了这一句:

316 userPrimaryGeneratorAction->GeneratePrimaries(anEvent);

这里的userPrimaryGeneratorAction就是之前SetUserAction时传入的指针。我们调用了GeneratePrimaries()函数,这个函数里面最关键的是这一句话:

particleGun->GeneratePrimaryVertex(anEvent);

在执行这句话之前,GeneratePrimaries()通过

particleGun->SetParticlePosition(G4ThreeVector(xpos, ypos, zpos));

已经设定好了初级顶点的相关参数。所以,为了修改初级顶点的位置,我们需要在BeamOn之前修改PrimaryGeneratorAction类的xpos、ypos、zpos成员变量。

通过前面的只是,我们知道可以通过messenger(例如:PrimaryGeneratorMessenger)的SetNewValue()成员函数来完成任务。但是有几个难点:

什么时候new PrimaryGeneratorMessenger?必须在执行修改的G4UIcommand之前

PrimaryGeneratorMessenger如何取得userPrimaryGeneratorAction指针?

取得指针之后如何修改其xpos、ypos、zpos成员变量?

我们不按顺序来回答这几个问题。

假设我们已经取得了userPrimaryGeneratorAction指针为,我们在PrimaryGeneratorAction类中写三个函数 setXPos()、setYPos()、setZPos(),并且通过userPrimaryGeneratorAction->setXPos()等命令来修改xpos等成员变量。

我们在PrimaryGeneratorMessenger的构造函数中传入PrimaryGeneratorAction的指针:

new PrimaryGeneratorMessenger(PrimaryGeneratorAction* myPGAclass)

为了把userPrimaryGeneratorAction传给PrimaryGeneratorMessenger

一种解决方案是把new PrimaryGeneratorAction()的结果用一个指针保存起来,而不是直接传进runManager->SetUserAction(),并且new PrimaryGeneratorMessenger(myPrimaryGeneratorAction)另一种方法是为PrimaryGeneratorAction增加一个PrimaryGeneratorMessenger类的成员函数,并且在其构造函数中new PrimaryGeneratorMessenger(this);这样,我们就完成了自己的Messenger的部署与使用。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值