EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
EicEventGenerator.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file EicEventGenerator.cxx
1 //
2 // AYK (ayk@bnl.gov), 2013/06/12
3 //
4 // Interface to eic-smear import classes;
5 //
6 
7 #include <assert.h>
8 #include <iostream>
9 
10 using std::cout;
11 using std::endl;
12 
13 #include "FairPrimaryGenerator.h"
14 #include <FairRootManager.h>
15 #include <FairRunSim.h>
16 
17 #include "EicEventGenerator.h"
18 
20 
21 #ifdef _PROMC_
22 #include "ProMC.pb.h"
23 #include "ProMCBook.h"
24 #endif
25 
26 #ifdef _EICMC_
27 #include <EicMCreader.h>
28 #endif
29 
30 // =======================================================================================
31 
32 Poacher::Poacher(const TString &fileName)
33 {
34  // Will not hurt anyway;
35  SetInputFileName(std::string(fileName.Data()));
36 
38 
39  //printf("%s\n", fileName.Data());
40  if (fileName.EndsWith(".promc")) {
41 #ifdef _PROMC_
42  // Open ProMC file; get access to the header fields;
43  mProMCBook = new ProMCBook(fileName.Data(), "r");
44  // NB: in fact the above constructor terminates the program if file can not be opened;
45  if (mProMCBook) {
46  ProMCHeader header = mProMCBook->getHeader();
47 
48  mMomentumUnits = (double)(header.momentumunit());
49  mLengthUnits = (double)(header.lengthunit());
50  }
51  else
52  Fatal("Poacher::Poacher()", "failed to open input ProMC file.");
53 
54  // Allocate buffer variables;
56  mEventProMC = new EventProMC();
57 #else
58  Fatal("Poacher::Poacher()", "ProMC support is not compiled in.");
59 #endif
60  } else if (fileName.EndsWith(".eicmc")) {
61 #ifdef _EICMC_
62  GOOGLE_PROTOBUF_VERIFY_VERSION;
63 
64  // Open EicMC file; get access to the header fields;
65  mReaderEicMC = new EicMC::Reader(fileName.Data());
66 
67  // Allocate buffer variables;
69  mEventEicMC = new EventEicMC();
70 #else
71  Fatal("Poacher::Poacher()", "EicMC support is not compiled in.");
72 #endif
73  } else {
74  // Forester on some generator ASCII file input; open file and jump to the first event;
75  // FIXME: there must be a memory leak somewhere here; works with absolute path only (?);
76  OpenInput();
77 
79  } //if
80 } // Poacher::Poacher()
81 
82 // ---------------------------------------------------------------------------------------
83 
85 {
86  if (mProMCBook) {
87 #ifdef _PROMC_
88  // Check, that there are still events in th efile;
89  if (mProMCBook->next() != 0) return 0;
90 
91  // This also resets the particle list;
92  mEventProMC->Clear();
93 
94  // Get ProMC event from file, loop through all the particles
95  // and populate erhic::EventMC, which has a format, understandable to the rest of the code;
96  ProMCEvent eve = mProMCBook->get();
97  ProMCEvent_Particles *pa=eve.mutable_particles();
98  // Well, I have to preserve consistency in the particle array (at least not
99  // to screw up parent-daughter relationship); so put ALL the particles in the
100  // event record; EicEventGenerator::ReadEvent() will take care to select only
101  // those with code '1' for transport and to create proper mapping table;
102  for (int j=0; j<pa->pdg_id_size(); j++) {
103  // Assign 4-momentum; units seemingly become [GeV] here, fine;
104  mParticle->Set4Vector((1.0/mMomentumUnits)*TLorentzVector(pa->px(j), pa->py(j), pa->pz(j), pa->energy(j)));
105 
106  // Assign vertex; FIXME: seemingly this way I obtain [cm]?;
107  mParticle->SetVertex((0.1/mLengthUnits)*TVector3(pa->x(j), pa->y(j), pa->z(j)));
108 
109  // Assign status and PDG;
110  mParticle->SetStatus((int)pa->status(j));
111  mParticle->SetId(pa->pdg_id(j));
112 
113  // Assign parent (assume a single one, sorry); base-1 here and below, right?;
114  mParticle->SetParentIndex(pa->mother1(j));
115  // Assign daughter range;
116  mParticle->SetChild1Index(pa->daughter1(j));
117  mParticle->SetChildNIndex(pa->daughter2(j));
118 
119  // NB: this call will dereference the pointer and create another instance, so
120  // hopefully there is no screw up with re-using the same pointer here;
122 
123  //printf("%4d (%4d) (%5d) -> %8.2f %8.2f %8.2f ... %8.2f %8.2f %8.2f\n",
124  // j, mParticle->GetStatus(), mParticle->Id().Code(),
125  // mParticle->GetPx(), mParticle->GetPy(), mParticle->GetPz(),
126  // mParticle->GetVertex().X(), mParticle->GetVertex().Y(), mParticle->GetVertex().Z());
127  } //for j
128 
129  return mEventProMC;
130 #else
131  Fatal("Poacher::Poacher()", "ProMC support is not compiled in."); return 0;
132 #endif
133  } else if (mReaderEicMC) {
134 #ifdef _EICMC_
135  // FIXME: allocate once;
136  eicmc::Record record;
137 
138  // Get next event record; return 0 if end-of-file encountered;
139  if (mReaderEicMC->GetNextEvent(record)) return 0;
140 
141  const eicmc::Record::MonteCarloEvent &mcevent = record.mcevent();
142 
143  for(int pt=0; pt<mcevent.particles_size(); pt++) {
144  const eicmc::Record::MonteCarloEvent::Particle &particle = mcevent.particles(pt);
145 
146  // Assign 4-momentum; FIXME: energy component!;
147  mParticle->Set4Vector(TLorentzVector(particle.px(), particle.py(), particle.pz()));
148 
149  // Assign vertex;
150  mParticle->SetVertex(TVector3(particle.vx(), particle.vy(), particle.vz()));
151 
152  // Assign status and PDG;
153  mParticle->SetStatus((int)particle.status());
154  mParticle->SetId(particle.pdg());
155 
156  // Assign parent (assume a single one, sorry); base-1 here and below, right?;
157  mParticle->SetParentIndex(particle.mother1());
158  // Assign daughter range;
159  mParticle->SetChild1Index(particle.daughter1());
160  mParticle->SetChildNIndex(particle.daughter2());
161 
162  // NB: see the comment in ProMC section; same story here;
164  } //for pt
165 
166  return mEventEicMC;
167 #else
168  Fatal("Poacher::Poacher()", "EicMC support is not compiled in."); return 0;
169 #endif
170  } else if (mFactory) {
171  return mFactory->Create();
172  }
173  else
174  return 0;
175 } // Poacher::GetNextEvent()
176 
177 // ---------------------------------------------------------------------------------------
178 
179 std::string Poacher::EventName() const
180 {
181  if (mProMCBook) {
182  return EventProMC::Class()->GetName();
183  } else if (mReaderEicMC) {
184  return EventEicMC::Class()->GetName();
185  } else if (mFactory) {
186  return mFactory->EventName();
187  } else {
188  assert(0); //return "";
189  } //if
190 } // Poacher::EventName()
191 
192 // =======================================================================================
193 
194 //EicEventGenerator::EicEventGenerator(TString fileName): FairGenerator()
195 EicEventGenerator::EicEventGenerator(TString fileName): EicProtoGenerator("EicEventGenerator")
196 {
197  //if (mInstance) {
198  //Fatal("EicEventGenerator::EicEventGenerator()", "Singleton instance already exists.");
199  //return;
200  //} //if
201  mInstance = this;
202 
203  cout << "-I EicEventGenerator: Using input file(s) " << fileName << endl;
204 
205  // Reset private variables to 0;
206  ResetVars();
207 
208  // This basically will be a default constructor;
209  if (fileName.IsNull()) return;
210 
211  // Yes, either ".root" suffix (ROOT file(s)) or any other extension
212  // and then it is some generator output ASCII file;
213  if (fileName.EndsWith(".root"))
214  {
215  mInputTree = new TChain(_EIC_GENERATOR_TREE_);
216  // NB: this will work on either single file or a mask like "dir/*.root";
217  mInputTree->Add(fileName);
218 
220  }
221  else
222  // If anything goes wrong here, Forester will throw an exception;
223  mPoacher = new Poacher(fileName);
224 
226 } // EicEventGenerator::EicEventGenerator()
227 
228 // ---------------------------------------------------------------------------------------
229 
230 //
231 // Well, the implementation is such, that this call can happen at any time
232 // and more than once; not really needed, but do not want to keep track of >1
233 // calls either; let user be responsible;
234 //
235 
237 
238 int EicEventGenerator::SkipFewEvents(unsigned eventsToSkip)
239 {
240  if (mPoacher) {
241  // Have to loop through the ASCII file;
242  for(unsigned ev=0; ev<eventsToSkip; ev++)
243  if (!mPoacher->GetNextEvent())
244  return -1;
245  }
246  else {
247  // In case of ROOT tree input just increment next event counter;
248  mInputTreeNextEventId += eventsToSkip;
249  //printf(" --> will start from %d\n", mInputTreeNextEventId); exit(0);
250  // Well, allow some control over what's going on;
251  if (!mInputTree->GetEntry(mInputTreeNextEventId)) return -1;
252  } //if
253 
254  return 0;
255 } // EicEventGenerator::SkipFewEvents()
256 
257 // ---------------------------------------------------------------------------------------
258 
260 {
261  // Clear re-mapping vector;
262  mMappingTable->mData.clear();
263 
264  // Fill out the next event record; NB: if PDG selection is made, it can happen, that
265  // there are no primary particles selected; therefore loop until find an event with
266  // at least one particle;
267  for( ; ; ) {
268  // Check, that event chunk size was not exceeded;
270  cout << "-W- EicEventGenerator(): event chunk size limit reached!" << endl;
271 
272  return kFALSE;
273  } //if
274 
275  if (mPoacher)
277  else {
278  //printf("%6d ...\n", mInputTreeNextEventId);
279  if (!mInputTree->GetEntry(mInputTreeNextEventId++)) mGeneratorEvent = 0;
280  } //if
281 
282  if (!mGeneratorEvent) {
283  cout << "-E- EicEventGenerator(): event list exhausted!" << endl;
284 
285  return kFALSE;
286  } //if
287 
288  // NB: this counter does not care about event selections, it just counts
289  // all considered input event candidates;
290  mEventCounter++;
291 
292  // If only leading particle is requested, have to arrange a separate loop;
293  int leadingParticleId = -1;
294  double leadingParticleMomentum;
296  for(unsigned iq=0; iq<mGeneratorEvent->GetNTracks(); iq++) {
298 
299  // May want to do it better later; suffices for now;
300  if (vp->GetStatus() != 1) continue;
301 
302  // Well, if PDG selection was applied, check that this particle matches;
303  if (mSelectedPdgCodes.size() && mSelectedPdgCodes.find(vp->Id()) == mSelectedPdgCodes.end())
304  continue;
305 
306  if (leadingParticleId == -1 || vp->GetP() > leadingParticleMomentum) {
307  leadingParticleId = iq;
308  leadingParticleMomentum = vp->GetP();
309  } //if
310  } //for iq
311 
312  // Well, if was not able to select a proper leading particle, skip to next event;
313  if (leadingParticleId == -1 ||
314  (mSelectedLeadingParticlePmin && leadingParticleMomentum < mSelectedLeadingParticlePmin))
315  continue;
316  } //if
317 
318  {
319  //erhic::EventDpmjet *evt = dynamic_cast<erhic::EventDpmjet*>(mGeneratorEvent);
320  //assert(evt);
321  //printf(" @@@ -> %d %d\n", evt->process1, evt->process2);
322 
323  }
324 
325  // Will be the same for all tracks;
326  TVector3 vtx = GetSimulatedPrimaryVertex();
327 
328  //printf("%d\n", mGeneratorEvent->GetNTracks()); fflush(stdout);
329  // Loop through all the particles and feed them to the FairRoot primary generator;
330  for(unsigned iq=0; iq<mGeneratorEvent->GetNTracks(); iq++) {
332 
333  // May want to do it better later; suffices for now;
334  if (vp->GetStatus() != 1) continue;
335 
336  // Well, if PDG selection was applied, check that this particle matches;
337  if (mSelectedPdgCodes.size() && mSelectedPdgCodes.find(vp->Id()) == mSelectedPdgCodes.end())
338  continue;
339 
340  // If leading particle was selected, check exact match;
341  if (leadingParticleId != -1 && leadingParticleId != iq) continue;
342 
343 
344 
345  //if (vp->GetParentIndex() != 6) continue;
346 
347 
348 
349  // Record explicitely all entries in the original erhic::ParticleMC* array
350  // which made it into the GEANT tracing engine; if direct encoding proves to
351  // be inconvenient, create inverse table or perhaps a 0/1 map;
352  mMappingTable->mData.push_back(iq);
353 
354  // Populate energy flow plot if requested;
355  if (mEnergyFlowPdgCodes.find(vp->Id()) != mEnergyFlowPdgCodes.end()) {
356  //printf(" --> %7.2f; %7.2f %7.2f -> %7.2f\n", vp->GetEta(),
357  // 1000*vp->GetE(), 1000*vp->GetM(), 1000*(vp->GetE() - vp->GetM()));
358  if (mEnergyFlowHist) mEnergyFlowHist ->Fill(vp->GetEta(), vp->GetE() - vp->GetM());
359  if (mParticleCountHist) mParticleCountHist->Fill(vp->GetEta());
360  } //if
361 
362  // Indeed some of the tracks may originate from a secondary vertex -> calculate a sum;
363  TVector3 pvtx = vtx + vp->GetVertex();
364 
365  // FIXME: at some point check that vtx units match;
366  {
367  TVector3 pvect = GetModifiedTrack(TVector3(vp->GetPx(), vp->GetPy(), vp->GetPz()));//vp->GetP());
368  //primGen->AddTrack(vp->Id(), vp->GetPx(), vp->GetPy(), vp->GetPz(), vtx[0], vtx[1], vtx[2]);
369  printf("%7.3f %7.3f %7.3f ... %4d %2d\n", pvect[0], pvect[1], pvect[2], vp->GetParentIndex(), vp->GetStatus());
370  //printf("%7.3f %7.3f %7.3f\n", vp->GetPx(), vp->GetPy(), vp->GetPz());//pvect[0], pvect[1], pvect[2]);
371  primGen->AddTrack(vp->Id(), pvect[0], pvect[1], pvect[2], pvtx[0], pvtx[1], pvtx[2]);
372  }
373  } /*for iq*/
374  //printf("stat: %3d -> %3d\n", mGeneratorEvent->GetNTracks(), mMappingTable->mData.size());
375 
376  // There are primary tracks selected in this event -> break;
377  if (mMappingTable->mData.size()) break;
378  } //for inf
379 
380  //
381  // THINK: for now the idea is that in case input file had no events, output tree/branch(es)
382  // are not created at all; re-shuffle "outputBranch" assignment, etc if this
383  // ever becomes a problem;
384  //
385 
386  // It looks like the easiest way is to always copy over the original tree into the same
387  // simulation.root output file; it is indeed a duplication in case of .root input -> FIXME;
388  // NB: just to mention it: output simulation.root file can be re-used as ROOT EICTree input file;
389  {
390  static TBranch *outputEventBranch, *outputMappingBranch;
391 
392  if (!outputEventBranch) {
393  TTree *outputTree = new TTree(_EIC_GENERATOR_TREE_, "A tree of original Monte Carlo events");
394  //outputTree->SetCurrentFile("simulation.root");
395 
396  // The easiest: do not switch pointers all the time; THINK: multi-threading?;
397  outputEventBranch = outputTree->Branch(_EIC_GENERATOR_EVENT_BRANCH_,
398  // Take real class name (like erhic::EventMilou) either
399  // from poacher (ASCII input)
400  // or from the input branch directly (.root input);
401  mPoacher ? mPoacher->EventName().c_str() :
402  mInputTree->GetBranch(_EIC_GENERATOR_EVENT_BRANCH_)->GetClassName(),
403  &mGeneratorEvent, 32000, 99);
404 
405  outputMappingBranch = outputTree->Branch(_EIC_MAPPING_BRANCH_, "ParticleMappingTable",
406  &mMappingTable, 32000, 99);
407 
408  // Arrange a separate FairTask with the only purpose to call Write() at the end of
409  // simulation run; FairRunSim::Instance() pointer should indeed be always available, or?;
411  } //if
412 
413  outputEventBranch->GetTree()->Fill();
414  }
415 
416  return kTRUE;
417 } // EicEventGenerator::ReadEvent()
418 
419 // ---------------------------------------------------------------------------------------
420 
422 {
423  if (mOutputTree) {
424  //printf(" --> writing to %s ...\n", mOutputTree->GetCurrentFile()->GetName());
425  mOutputTree->GetCurrentFile()->cd();
426  mOutputTree->Write();
427 
429  if (evtGen && evtGen->mEventCounter) {
430  TH1D *hists[2] = {evtGen->mParticleCountHist, evtGen->mEnergyFlowHist};
431 
432  for(unsigned ih=0; ih<2; ih++) {
433  TH1D *hist = hists[ih];
434 
435  if (hist) {
436  hist->Scale(1.0/(evtGen->mEtaBinWidth*evtGen->mEventCounter));
437  hist->Write();
438  } //if
439  } //for ih
440  } //if
441  } //if
442 
444 } // EicEventGeneratorTask::FinishTask()
445 
446 // ---------------------------------------------------------------------------------------
447 
451 
454