이번 프로그램에서는 Run Action을 입력과 출력의 도구로 활용할 계획이다. 사실 두번째 프로그램에서도 G4Tool을 OTRunAction에서 사용하여 출력의 도구로 활용을 하였는데 이번에는 입력도 추가할 계획이다.
Multi-treading 에 관하여 토픽에서 언급하였듯이 출력에 해당하는 G4Tool은 스레드 안정성을 보장하지만, 이벤트 파일을 읽는 경우는 매우 까다롭다. 그 이유는 이 이능을 클래스로 만들었을 때,
- 객체는 프로세스 안에서 유일해야하고
- 모든 스레드에서 접근해야 하기 때문에 스레드 안정성을 고려해야 한다.
- 그리고 모든 스레드가 이 객체에 쉽게 접근할 수 있어야 한다.
따라서 이벤트 리더 클래스는 마스터 스레드의 Run Action을 사용하기로 한다. 마스터 스레드의 Run Action은 다른 모든 스레드의 Run Action이 초기화 되기 전에 초기화를 하며 유일하고
G4MTRunManager::GetMasterRunManager() -> GetUserRunAction();
와 같이 Geant4 프로그램 어디서든 쉽게 불러올 수 있다.
사실 Action 클래스의 IsMaster() 함수를 이용하면 객체가 마스터인지 아닌지 알 수 있기때문에 하나의 OTRunAction을 고치면 파일을 추가 하지 않고 프로그램을 만들 수 있지만 파일이 길어지고 역할이 나뉘어서 가독성이 떨어지는 것을 방지하기 위하여 OTMasterRunAction 클래스를 하나 더 만들기로 한다.
마스터 Run Action
마스터 Run Action 이란 전 토픽에서 소개한 Action Initialization 클래스의 BuildForMaster() 에서 SetUserAction() 함수를 통하여 만들어주는 Run Action 객체에 해당한다.
void OTActionInitialization::BuildForMaster() const { SetUserAction(new OTMasterRunAction("../primaries.gen")); }
이 BuildForMaster() 함수에서 생성한 객체가 바로 아래에서 가져오는 Run Action 객체에 해당한다.
G4MTRunManager::GetMasterRunManager() -> GetUserRunAction();
OTMasterRunAction이 조금 더 중요해진 이유는 OTPrimaryAction에서 이벤트 생성(GeneratePrimaries)을 가져왔기 때문이다. 가장 큰 이유는 스레드 안정성 때문인데 어짜피 파일은 OTMasterRunAction에서 읽어올 것이라면 그 정보를 다시 OTPrimaryGeneratorAction 으로 가져와서 이벤트 생성을 하지 않고 직접 생성하는 편이 간결하기 때문이다. 이부분은 천천히 알아가자.
이벤트 파일
먼저 우리가 사용하게 될 이벤트 파일의 포멧을 살펴보자.
0 [number of tracks] [vx] [vy] [vz]
[PDG] [kinetic energy] [px] [py] [pz]
[PDG] [kinetic energy] [px] [py] [pz]
1 [number of tracks] [vx] [vy] [vz]
[PDG] [kinetic energy] [px] [py] [pz]
[PDG] [kinetic energy] [px] [py] [pz]
...
[number of events - 1] [number of tracks] [vx] [vy] [vz]
[PDG] [kinetic energy] [px] [py] [pz]
[PDG] [kinetic energy] [px] [py] [pz]
위 파일은 처음에 이벤트의 개수가 적혀있고, 다음줄부터 이벤트 블록이 연속적으로 들어간다. 이벤트 블록의 내용은 첫줄이
이며 여기서 vx, vy, vz는 입자의 첫 위치(vertex) 를 의미한다. 이벤트 블록의 두번째 줄부터[number of tracks] 만큼의 입자의 정보가 연속해서 나온다.
px, py, pz는 운동량의 방향을 의미한다. 그 다음에 다음 이벤트 블록이 나오는 식이다. 예제로 넣어둔 primaries.gen 파일의 첫 몇줄 부분을 보면 다음과 같다.
0 2 0 0 0
2212 1.33712 0.00327969 -0.00167218 0.999993
2212 1.42812 -0.0105228 0.00463264 0.999934
1 1 0 0 0
2212 1.29049 0.00504613 0.0022915 0.999985
2 1 0 0 0
2212 1.38268 -0.00952423 0.00411158 0.999946
...
우리가 사용할 이벤트의 개수는 10000개 이므로 매크로 run.mac을 만들어서 이벤트 10000개 실행 명령을 적어 두도록 하자. 그리고 편의를 위해서 CMakeLists.txt에도 적어둔다.
파일 읽기
OTMasterRunAction 의 생성자에서 바로 파일을 읽기 시작한다. 파일을 읽기 위해서 c++ standard library 의 ifstream 을 사용하였다.
OTMasterRunAction::OTMasterRunAction(const char *fileName) // for master : G4UserRunAction() { fInputFile.open(fileName); fInputFile >> fNumEvents; G4cout << "Number of events in file: " << fNumEvents << G4endl; fParticleGun = new G4ParticleGun(1); }
여기서는 파일의 첫부분인 이벤트 개수만 읽어온다. 이후 G4Event를 받아오는 새로운 함수를 만들고OTPrimaryGeneratorAction 에서 이벤트 생성을 대신 해주도록 G4Event를 넘겨주기만 하면 된다.
bool OTMasterRunAction::GeneratePrimaries(G4Event *anEvent) { G4int eventID, numTracks; G4double vx, vy, vz; if (!(fInputFile >> eventID >> numTracks >> vx >> vy >> vz)) return false; if (numTracks <= 0) return false; G4int eventCheck = G4int(fNumBeamOn/10); if (eventCheck == 0) eventCheck = 1; if (eventID % eventCheck == 0) G4cout << eventID << " / " << fNumBeamOn << G4endl; fParticleGun -> SetParticlePosition(G4ThreeVector(vx,vy,vz)); G4int pdg; G4double ke, px, py, pz; for (G4int iTrack = 0; iTrack < numTracks; ++iTrack) { fInputFile >> pdg >> ke >> px >> py >> pz; G4ParticleDefinition* particle = G4ParticleTable::GetParticleTable() -> FindParticle(pdg); G4ThreeVector direction(px,py,pz); fParticleGun -> SetParticleDefinition(particle); fParticleGun -> SetParticleEnergy(ke*MeV); fParticleGun -> SetParticleMomentumDirection(direction.unit()); fParticleGun -> GeneratePrimaryVertex(anEvent); } return true; }
이벤트 생성에 대한 내용의 대부분은 Primary Generator Action 토픽에서 설명되어 있으므로 넘어간다.
이벤트가 많을 때 시뮬레이션이 어느정도 진행되었는지 알고싶기 때문에 실행한 이벤트의 10% 만큼 진행될 때 마다 출력을 하도록 코딩을 하였다. 처음 실행한 이벤트의 개수는 아래와 같이 Master Run Manager의 GetNumberOfEventsToBeProcessed() 함수로 받아 올 수 있다.
void OTMasterRunAction::BeginOfRunAction(const G4Run*) { fNumBeamOn = G4MTRunManager::GetMasterRunManager() -> GetNumberOfEventsToBeProcessed(); G4cout << "Number of events in file: " << fNumEvents << G4endl; G4cout << "BeamOn " << fNumBeamOn << G4endl; }
실행한 이벤트의 개수(코드 상의 fNumBeamOn)는 파일에 들어있는 이벤트의 개수(코드 상의fNumEvents)와 다를 수 있으므로 주의 하자. 이 프로그램에서 실행한 이벤트의 개수는 run.mac 메크로의 /run/beamOn 명령에 의해서 결정된다.