Geant4 가이드

Run Action, 이벤트 파일

본 토픽은 현재 준비중입니다.공동공부에 참여하시면 완성 되었을 때 알려드립니다.
이벤트 파일을 읽을 필요가 없다면 Primary Generator Action을 고칠필요가 없으며 토픽을 넘어가도 무방하다.

이번 프로그램에서는 Run Action을 입력과 출력의 도구로 활용할 계획이다. 사실 두번째 프로그램에서도 G4Tool을 OTRunAction에서 사용하여 출력의 도구로 활용을 하였는데 이번에는 입력도 추가할 계획이다. 

 Multi-treading 에 관하여 토픽에서 언급하였듯이 출력에 해당하는 G4Tool은 스레드 안정성을 보장하지만, 이벤트 파일을 읽는 경우는 매우 까다롭다. 그 이유는 이 이능을 클래스로 만들었을 때,

  1. 객체는 프로세스 안에서 유일해야하고
  2.  모든 스레드에서 접근해야 하기 때문에 스레드 안정성을 고려해야 한다.
  3. 그리고 모든 스레드가 이 객체에 쉽게 접근할 수 있어야 한다.

따라서 이벤트 리더 클래스는 마스터 스레드의 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 으로 가져와서 이벤트 생성을 하지 않고 직접 생성하는 편이 간결하기 때문이다. 이부분은 천천히 알아가자.

소스헤더를 참고하자.

이벤트 파일

먼저 우리가 사용하게 될 이벤트 파일의 포멧을 살펴보자.

[number of events]
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]

위 파일은  처음에 이벤트의 개수가 적혀있고, 다음줄부터 이벤트 블록이 연속적으로 들어간다. 이벤트 블록의 내용은 첫줄이

[event-ID] [number of tracks] [vx] [vy] [vz]

이며 여기서  vx, vy, vz는 입자의 첫 위치(vertex) 를 의미한다. 이벤트 블록의 두번째 줄부터[number of tracks] 만큼의 입자의 정보가 연속해서 나온다.

[PDG] [kinetic energy] [px[py] [pz]

px, py, pz는 운동량의 방향을 의미한다. 그 다음에 다음 이벤트 블록이 나오는 식이다. 예제로 넣어둔 primaries.gen 파일의 첫 몇줄 부분을 보면 다음과 같다.

10000
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 명령에 의해서 결정된다.

  • 봤어요 0명

댓글

댓글 본문