EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
EicEventAssembler.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file EicEventAssembler.cxx
1 //
2 // AYK (ayk@bnl.gov), 2014/07/08
3 //
4 // Basically a task filling out EicRcTrack, EicRcVertex, etc
5 // arrays and do all dirty work to collect the necessary info;
6 
7 #include <iostream>
8 using namespace std;
9 
10 #include <PndPidCandidate.h>
11 #include <PndMCTrack.h>
12 
13 #include <EicRootManager.h>
14 #include <EicRcParticle.h>
15 #include <EicEventAssembler.h>
17 
18 // ---------------------------------------------------------------------------------------
19 
21  mPndMCTracks(0),
22  mPndPidChargedCand(0),
23  mMappingTable(0),
24  mGeneratorEvent(0),
25  mEicRcEvent(0),
26  mPersistency(kTRUE),
27  mEicRcEventBranch(0),
28  FairTask("EIC EVENT ASSEMBLER")
29 {
30 
31 } // EicEventAssembler::EicEventAssembler()
32 
33 // ---------------------------------------------------------------------------------------
34 
36 {
37  // FIXME: consider to check for double-counting;
38  mCalorimeters.push_back(new EicCalorimeterHub(name, type));
39 
40  return 0;
41 } // EicEventAssembler::AddCalorimeterCore()
42 
43 // ---------------------------------------------------------------------------------------
44 
46 {
47  return AddCalorimeterCore(name, qEmCal);
48 } // EicEventAssembler::AddEmCal()
49 
50 // ---------------------------------------------------------------------------------------
51 
53 {
54  return AddCalorimeterCore(name, qHCal);
55 } // EicEventAssembler::AddHCal()
56 
57 // ---------------------------------------------------------------------------------------
58 
60 {
61  // Get access to arrays with PandaRoot MC tracks and charged track candidates;
63 
64  if ( !ioman ) {
65  cout << "-E- " << "EicEventAssembler::Init(): "
66  << "FairRootManager not instantiated!" << endl;
67  return kFATAL;
68  } //if
69 
70  mPndMCTracks = (TClonesArray*)ioman->GetObject(_PND_MC_BRANCH_);
71  if ( !mPndMCTracks ) {
72  std::cout << "-E- EicSmearTask::Init: No MCTrack array found!" << std::endl;
73  return kERROR;
74  } //if
75 
76  mPndPidChargedCand = (TClonesArray *)ioman->GetObject(_PND_RC_TRACK_BRANCH_);
77 
78  // Synchronize EICTree (generator info) by hand (if present);
79  if (ioman->GetInFile()->Get(_EIC_GENERATOR_TREE_)) {
80  TChain *extraTree = new TChain(_EIC_GENERATOR_TREE_);
81  extraTree->Add(ioman->GetInFile()->GetName());
82 
83  extraTree->SetBranchAddress(_EIC_GENERATOR_EVENT_BRANCH_, &mGeneratorEvent);
84  extraTree->SetBranchAddress(_EIC_MAPPING_BRANCH_, &mMappingTable);
85 
86  ioman->GetInTree()->AddFriend(extraTree);
87  } //if
88 
89 #if 1
90  //fRcParticleArray = new TClonesArray("EicRcParticle");
91  //ioman->Register("EicRcParticle", "EEA", fRcParticleArray, fPersistence);
92 
93  {
96 
97  if ( !io ) {
98  cout << "-E- " << "EicEventAssembler::Init(): "
99  << "EicRootManager failed to instantiate!" << endl;
100  return kFATAL;
101  } //if
102  }
103 #endif
104 
105  return kSUCCESS;
106 } // EicEventAssembler::Init()
107 
108 // ---------------------------------------------------------------------------------------
109 
111 {
112  // Loop through all particles;
113  for(unsigned pt=0; pt<mEicRcEvent->mParticles.size(); pt++) {
115 
116  // Loop through all the contributing hits; for now do not make any attempt to
117  // decouple shared hits; FIXME: a more intelligent algorithm is needed later;
118  for(unsigned cl=0; cl<particle->mCellGroups.size(); cl++) {
119  std::pair<unsigned, unsigned> value = particle->mCellGroups[cl];
120  EicCalorimeterHub *chub = mCalorimeters[value.first];
121  // Depending on calorimeter type populate either EmCal or HCal variables;
122  // CHECK: qUndefined can not happen, right?;
123  EicRcCalorimeterHit **ptrHit = chub->mType == qEmCal ? &particle->mEmCal : &particle->mHCal;
124  if (!*ptrHit) *ptrHit = new EicRcCalorimeterHit();
125  EicRcCalorimeterHit *hit = *ptrHit;
126 
127  // Fine, now I have this pointer defined; proceed further; get cluster group instance;
128  CalorimeterCellGroup *group = (CalorimeterCellGroup *)chub->mClusters->At(value.second);
129 
130  // Loop through all the members and select ones matching mPndMCTrackIndex;
131  for(std::map<std::pair<UInt_t, UInt_t>, Double_t>::iterator it=group->mEnergyPerParent.begin();
132  it != group->mEnergyPerParent.end(); it++) {
133  // Artificial match: require that "true" sub-cluster parent has the same PANDA
134  // MC index as the charged track; once at least one such sub-cluster found, break;
135  if (it->first.second == particle->mPndMCTrackIndex) {
136  hit->mEnergy += it->second;
137 
138  // Assume, that there is a generic calorimeter-specific call which gives energy
139  // error estimate; may want to do cluster-specific call later if needed;
140  double sigma = chub->EnergyErrorEstimate(it->second);
141  hit->mEnergySigma += sigma * sigma;
142 
143  //@@@particle->mRcEmCalHitLocation += hit->mEnergy * hit->mLocation;
144  } //if
145  } //for it (group->mEnergyPerParent)
146  } //for cl
147 
148  // Renormalize energy resolution estimates;
149  for(unsigned eh=0; eh<2; eh++) {
150  EicRcCalorimeterHit *hit = eh ? particle->mHCal : particle->mEmCal;
151 
152  if (hit) {
153  hit->mEnergySigma = sqrt(hit->mEnergySigma);
154 
155  //@@@particle->mRcEmCalHitLocation *= 1./particle->mRcEmCalEnergy;
156  } //if
157  } //for ct
158  } //for pt
159 } // EicEventAssembler::ComposeCalorimeterInformation()
160 
161 // ---------------------------------------------------------------------------------------
162 
163 #define _PHOTON_PDG_CODE_ 22
164 #define _PION_PDG_CODE_ 211
165 #define _POSITRON_PDG_CODE_ (-11)
166 
168 {
169  // For now simply loop through all particles, select the one with Generator index
170  // equal 2 (or PndMCTrack index equal 0) and let it be scattered lepton;
171  for(unsigned pt=0; pt<mEicRcEvent->mParticles.size(); pt++) {
173 
174  // Well, the logic is: no charged track -> let it be photon, no matter what
175  // the values of EmCal and HCal energies are;
176  if (particle->mPndPidChargedCandIndex == -1)
177  particle->mRcPdgCode = _PHOTON_PDG_CODE_;
178  else {
179  // Otherwise consider a crude e/p check for now; need to use sample
180  // lepton and hadron energy distributions in each calorimeter type
181  // to take a better decision;
182  PndPidCandidate* pidcand =
184 
185  // A clear hack for now; just want to check, that DisKinmatics calculator works;
186 #if 1
187  particle->mRcPdgCode = _POSITRON_PDG_CODE_;
188 #else
189  if (particle->mEmCal) {
190  double diff = particle->mEmCal->mEnergy - particle->mRcVtxMomentum.Mag();
191  double sigma = sqrt(particle->mEmCal->mEnergySigma * particle->mEmCal->mEnergySigma +
192  particle->mRcTrackerMomentumSigma * particle->mRcTrackerMomentumSigma);
193  // Assume any positive value is fine?;
194  if (diff > 0.0 || fabs(diff/sigma) < 3.0)
195  particle->mRcPdgCode = _POSITRON_PDG_CODE_;
196  else
197  particle->mRcPdgCode = _PION_PDG_CODE_;
198  }
199  else
200  particle->mRcPdgCode = _PION_PDG_CODE_;
201 #endif
202 
203  particle->mRcPdgCode *= pidcand->GetCharge();
204  } //if
205 
206  //@@@particle->mRcPdgCode = _PION_PDG_CODE_ * pidcand->GetCharge();
207  //@@@particle->mHadronPidCL.push_back(std::pair<EicRcParticle::HadronPidType, float>
208  //@@@ (EicRcParticle::PionKaonProton, 1.0));
209  //@@@
210  //@@@particle->mChargeDefCL = particle->mElectronPidCL = 0.0;
211 
212  //printf("%2d -> %d %d\n", pt, particle->mRcPdgCode, particle->GetMcPdgCode());
213  } //for pt
214 } // EicEventAssembler::PerformPidCalculations()
215 
216 // ---------------------------------------------------------------------------------------
217 
219 {
220  //
221  // FIXME: there is an implicit interplay between PerformPidCalculations() and
222  // ReAssignMomentumValue() in a sense they use the same input information to
223  // make logical assignments; perhaps makes sense to unify these two calls;
224  //
225 
226  for(unsigned pt=0; pt<mEicRcEvent->mParticles.size(); pt++) {
228 
229  // Trivial case: photon; this would mean no charged track and EmCal information
230  // available; just assign 3D momentum based on e/m cluster energy and location;
231  if ( particle->mRcPdgCode == _PHOTON_PDG_CODE_) {
232  // FIXME: think, what to do if no EmCal info (and no track!) available;
233  assert(particle->mEmCal);
234 
235  particle->mRcVtxMomentum = particle->mEmCal->mEnergy * particle->mEmCal->mLocation.Unit();
236  }
237  // Electron: assume tracking coverage is sufficient to claim, that in case no
238  // charged track seen in the detector, particle could not have been electron (and thus
239  // would be photon); if EmCal info available (and most likely it is and was used in
240  // e/p decision), use weighted method (mix tracking and e/m calorimeter info);
241  else if (abs(particle->mRcPdgCode) == _POSITRON_PDG_CODE_) {
242  // Otherwise do not do anything (particle->mRcVtxMomentum will remain as assigned by tracker);
243  if (particle->mEmCal) {
244  double wtE = 1./(particle->mEmCal->mEnergySigma*particle->mEmCal->mEnergySigma);
245  double wtP = 1./(particle->mRcTrackerMomentumSigma*particle->mRcTrackerMomentumSigma);
246  // Ignore electron mass, sorry;
247  double E = (wtE*particle->mEmCal->mEnergy + wtP*particle->mRcVtxMomentum.Mag())/(wtE+wtP);
248  // NB: direction still take from tracking;
249  particle->mRcVtxMomentum = E * particle->mRcVtxMomentum.Unit();
250  } //if
251  }
252  // For pions (no RICH and other hadron PID devices as of yet) assume charged track
253  // was always avaliable; yet try to account HCal+EmCal energy;
254  else if (abs(particle->mRcPdgCode) == _PION_PDG_CODE_) {
255  // Otherwise will remain as assigned by tracking;
256  if (particle->mHCal) {
257  // NB: this will also work if either one of the calorimeter parts (EmCal or HCal) was missing;
258  double fullCalorimeterEnergy = 0.0, fullCalorimeterEnergySigma = 0.0;
259  for(unsigned eh=0; eh<2; eh++) {
260  EicRcCalorimeterHit *hit = eh ? particle->mHCal : particle->mEmCal;
261 
262  fullCalorimeterEnergy += hit->mEnergy;
263  fullCalorimeterEnergySigma += hit->mEnergySigma;
264  } //for ct
265  fullCalorimeterEnergySigma = sqrt(fullCalorimeterEnergySigma);
266 
267  // FIXME: unify this part with electron case (see above);
268  double wtE = 1./(fullCalorimeterEnergySigma*fullCalorimeterEnergySigma);
269  double wtP = 1./(particle->mRcTrackerMomentumSigma*particle->mRcTrackerMomentumSigma);
270  // FIXME: ignore pion mass, sorry;
271  double E = (wtE*fullCalorimeterEnergy + wtP*particle->mRcVtxMomentum.Mag())/(wtE+wtP);
272  particle->mRcVtxMomentum = E * particle->mRcVtxMomentum.Unit();
273  } //if
274  } //if
275  } //for pt
276 } // EicEventAssembler::ReAssignMomentumValue()
277 
278 // ---------------------------------------------------------------------------------------
279 
281 {
282  // Get beam lepton charge (sign); FIXME: may want to assign once?;
283  const erhic::ParticleMC* beamLepton = mEicRcEvent->BeamLepton();
284  //printf("%p\n", beamLepton); assert(0);
285  if (!beamLepton) return;
286 
287  Int_t beamLeptonPDG = beamLepton->GetPdgCode().Info()->PdgCode();
288  //printf("beam lepton PDG: %d\n", beamLeptonPDG);
289  //printf("old sc.lepton ID: %d\n", mEicRcEvent->mScatteredLeptonID);
290 
292  for(unsigned pt=0; pt<mEicRcEvent->mParticles.size(); pt++) {
294  //printf("Beam PDG: %d; my PDG: %d\n", beamLeptonPDG, particle->mRcPdgCode);
295  if (particle->mRcPdgCode == beamLeptonPDG &&
296 #if 1
297  // Yes, for now want ti fake, sorry; no real PID and comparing energies
298  // only (since all charged particles are given +/-11 PDG code) is just misleading;
299  particle->GetMcPdgCode() == beamLeptonPDG &&
300 #endif
301  (mEicRcEvent->mScatteredLeptonID == -1 ||
302  particle->GetE() > mEicRcEvent->mParticles[mEicRcEvent->mScatteredLeptonID]->GetE()))
303 
305  } //for pt
306 
307  printf("scattered lepton: %2d\n", mEicRcEvent->mScatteredLeptonID);
308 } // EicEventAssembler::AssignScatteredLepton()
309 
310 // ---------------------------------------------------------------------------------------
311 
312 void EicEventAssembler::Exec(Option_t* opt)
313 {
314  //printf(" EicEventAssembler::Exec() %3d ...\n", mPndPidChargedCand->GetEntriesFast());
315 
316  //return;
317 
318 
319 
320  //static unsigned evcounter;
321  //evcounter++;
322 
323  if (!mEicRcEventBranch) {
324  TTree *outputTree = new TTree(_EIC_RC_TREE_, "A tree of reconstructed events");
325 
326  // The easiest: do not switch pointers all the time; THINK: multi-threading?;
327  mEicRcEventBranch = outputTree->Branch(_EIC_RC_BRANCH_, _EIC_RC_BRANCH_,
328  &mEicRcEvent, 32000, 99);
329  //printf(" --> will be writing to %s ...\n", outputTree->GetCurrentFile()->GetName()); exit(0);
330 
331  mEicRcEvent = new EicRcEvent();
332 
333  {
334  FairRootManager* ioman = FairRootManager::Instance(); assert(ioman);
335 
336  for(unsigned ct=0; ct<mCalorimeters.size(); ct++) {
337  EicCalorimeterHub *chub = mCalorimeters[ct];
338 
339  chub->mClusters = (TClonesArray *)ioman->GetObject(chub->mName->Name() + "ClusterGroup");
340  } //for ct (mCalorimeters)
341  }
342  } //if
343 
344  //rcEvent->ClearParticles();
345  mEicRcEvent->mParticles.clear();
346 
347  //fRcParticleArray->Clear();
348  //if (mtable) cout << "@@@ -> " << mtable->GetData().size() << endl;
349 
350  // First, loop through the reconstructed charged tracks and (no matter how they were
351  // reconstructed and whether mc-to-rc correspondence was established by hand)
352  // allocate one EicRcParticle instance per track;
353  if (mPndPidChargedCand)
354  for(unsigned ch=0; ch<mPndPidChargedCand->GetEntriesFast(); ch++) {
356 
357  // Allocate new reconstructed track;
359  //new((*fRcParticleArray)[fRcParticleArray->GetEntriesFast()]) EicRcParticle();
360 
361  // Assign PidChargedCand and (may be) MCTrack array indices;
362  particle->mPndPidChargedCandIndex = ch;
363 
364  // Pull out few most needed variables from PndMCTrack class;
365  int mcId = pidcand->GetMcIndex();
366  if (mcId >= 0) {
367  particle->mPndMCTrackIndex = mcId;
368 
369  // Well, it looks like no range checks, etc are needed here?;
370  if (mMappingTable) particle->mGeneratorEventIndex = mMappingTable->GetData()[mcId];
371 
372  // FIXME: may want to return back at least 'mctrack' assignment;
373  //PndMCTrack *mctrack = (PndMCTrack*)mPndMCTracks->At(mcId);
374  } //if
375 
376  // Pull out few most needed variables from PndPidCandidate class;
377  particle->mRcVertex = pidcand->GetPosition();
378  particle->mRcVtxMomentum = pidcand->GetMomentum();
379  particle->mRcTrackerMomentumSigma =
380  sqrt(pidcand->GetCov()[0])*pow(particle->mRcVtxMomentum.Mag(), 2);//*particle->mRcVtxMomentum;
381  //printf("%7.1f +/- %8.4f\n", particle->mRcVtxMomentum.Mag(), particle->mRcTrackerMomentumSigma);
382 
383  printf("%3d MC PDG: %5d (E=%7.3f) --> %3d\n", ch, particle->GetMcPdgCode(),
384  particle->GetE(), particle->mPndMCTrackIndex);
385 
386  mEicRcEvent->mParticles.push_back(particle);
387  } //if..for ch
388 
389  // Then loop through all calorimeter clusters (cell groups); at this stage want to
390  // relate track to *all* clusters which can potentially belong to it (even partly);
391  // this procedure can be either based on MC track and cluster "parent" IDs (as now)
392  // or done based on a match in space (later);
393 #if _LATER_
394  for(unsigned ct=0; ct<mCalorimeters.size(); ct++) {
395  EicCalorimeterHub *chub = mCalorimeters[ct];
396 
397  if (!chub->mClusters) continue;
398 
399  printf("Here: %d \n", chub->mClusters->GetEntriesFast());
400 
401  for(unsigned cl=0; cl<chub->mClusters->GetEntriesFast(); cl++) {
402  CalorimeterCellGroup *group = (CalorimeterCellGroup *)chub->mClusters->At(cl);
403 
404  bool found = false;
405 
406  // Carelessly loop through all the EicRcParticle instances and try to establish
407  // either track-to-cluster or cluster-to-cluster relation;
408  for(unsigned pt=0; pt<mEicRcEvent->mParticles.size(); pt++) {
410 
411  for(std::map<std::pair<UInt_t, UInt_t>, Double_t>::iterator it=group->mEnergyPerParent.begin();
412  it != group->mEnergyPerParent.end(); it++) {
413  // Artificial match: require that "true" sub-cluster parent has the same PANDA
414  // MC index as the charged track; once at least one such sub-cluster found, break;
415  if (it->first.second == particle->mPndMCTrackIndex) {
416  particle->mCellGroups.push_back(std::pair<unsigned, unsigned>(ct, cl));
417  found = true;
418 
419  // Yes, fall just into 'pt' loop and continue (there can be more than one
420  // charged particle track associated with this cluster group);
421  break;
422  } //if
423 
424  //printf("%3d %3d -> %f\n", it->first.first, it->first.second, it->second);
425  } //for it
426  _next_particle:
427  continue;
428  } //for pt
429 
430  // Unless at least one EicRcParticle instance match found, create a new one for every
431  // parent; yes, these instances will only contain this calorimeter info (yet mPndMCTrackIndex
432  // will be assigned);
433  if (!found) {
434  // Keep track on registered parents;
435  std::set<unsigned> registered;
436 
437  for(std::map<std::pair<UInt_t, UInt_t>, Double_t>::iterator it=group->mEnergyPerParent.begin();
438  it != group->mEnergyPerParent.end(); it++)
439  if (registered.find(it->first.second) == registered.end()) {
441 
442  particle->mPndMCTrackIndex = it->first.second;
443  particle->mCellGroups.push_back(std::pair<unsigned, unsigned>(ct, cl));
444  mEicRcEvent->mParticles.push_back(particle);
445 
446  registered.insert(it->first.second);
447  } //for it (group->mEnergyPerParent)..if
448  } //if
449  } //for cl
450  } //for ct (mCalorimeters)
451 
452  // Combine and unify information from all calorimeters;
454 #endif
455 
456  // Pretty naive (ideal) for now;
458 
459  // Based on PID assignments (re)calculate mRcVtxMomentum;
460 #if _LATER_
462 #endif
463 
464  // Basically check whether at least one electron (positron) with the same
465  // charge as beam lepton exist and select the one with the highest momentum;
467 
468  mEicRcEventBranch->GetTree()->Fill();
469 } // EicEventAssembler::Exec()
470 
471 // ---------------------------------------------------------------------------------------
472 
474 {
476  //@@@mEicRcEventBranch->GetTree()->GetCurrentFile()->cd();
477  mEicRcEventBranch->GetTree()->Write();
478  } //if
479 
480 #if _OFF_
481  outFile->cd();
482  smearedTree->Write();
483 #endif
484 
486 } // EicEventAssembler::FinishTask()
487 
488 // ---------------------------------------------------------------------------------------
489