EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TreeToHepMC.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file TreeToHepMC.cxx
1 
10 #include <string>
11 #include <iostream>
12 
13 #include <TString.h>
14 #include <TSystem.h>
15 #include <TBranch.h>
16 #include <TLeaf.h>
17 #include <TDatabasePDG.h>
18 
20 #include "eicsmear/erhic/File.h"
21 
22 #include "HepMC3/GenEvent.h"
23 #include "HepMC3/GenVertex.h"
24 #include "HepMC3/GenParticle.h"
25 #include "HepMC3/GenCrossSection.h"
26 #include "HepMC3/Attribute.h"
27 #include "HepMC3/Version.h"
28 #include "HepMC3/WriterAscii.h"
29 #include "HepMC3/WriterAsciiHepMC2.h"
30 #include "HepMC3/WriterRoot.h"
31 #include "HepMC3/WriterRootTree.h"
32 
33 #include <map>
34 
35 using std::cout;
36 using std::cerr;
37 using std::endl;
38 
39 using HepMC3::FourVector;
40 using HepMC3::GenRunInfo;
41 using HepMC3::GenEvent;
42 using HepMC3::GenParticle;
43 using HepMC3::GenParticlePtr;
44 using HepMC3::GenVertex;
45 using HepMC3::GenVertexPtr;
46 using HepMC3::GenCrossSection;
47 using HepMC3::GenCrossSectionPtr;
48 
49 // see include/eicsmear/functions.h for declaration and default values
50 
56 Long64_t TreeToHepMC(const std::string& inputFileName,
57  const std::string& outputDirName,
58  Long64_t maxEvent,
59  const erhic::HepMC_outtype outtype) {
60 
61  // Make sure this is a root file,
62  if ( !TString(inputFileName).EndsWith(".root", TString::kIgnoreCase) ){
63  cerr << "Warning: " << inputFileName << " does not end with .root" << endl;
64  }
65 
66  // Open the input file and get the Monte Carlo tree from it.
67  // Complain and quit if we don't find the file or the tree.
68  TFile inFile(inputFileName.c_str(), "READ");
69  if (!inFile.IsOpen()) {
70  std::cerr << "Unable to open " << inputFileName << std::endl;
71  } // if
72  TTree* mcTree(NULL);
73  // TODO: Extend to smeared trees
74  inFile.GetObject("EICTree", mcTree);
75  if (!mcTree) {
76  std::cerr << "Unable to find EICTree in " << inputFileName << std::endl;
77  return 1;
78  }
79  erhic::EventMC* inEvent(NULL);
80  mcTree->SetBranchAddress("event",&inEvent);
81 
82  // Get generator name
83  TClass* branchClass = TClass::GetClass(mcTree->GetBranch("event")->GetClassName());
84  TString generatorname = branchClass->GetName();
85  if (branchClass->InheritsFrom("erhic::EventDis")) {
86  generatorname.ReplaceAll("erhic::Event","");
87  } else {
88  cerr << branchClass->GetName() << " is not supported." << endl;
89  return -1;
90  } // if
91 
92  // BeAGLE is currently unfixable; using a kludge to salvage what we can
93  bool beaglemode=false;
94  if (branchClass->InheritsFrom("erhic::EventBeagle")) {
95  cout << "Warning: BeAGLE support is rudimentary. Can't fix mother-daughter structure." << endl;
96  cout << endl;
97  beaglemode=true;
98  }
99 
100  // Older Milou files need special treatment
101  bool legacymilou=false;
102  bool milouwarn=false; // Need to enter event loop to determine, but warn only once
103  if (branchClass->InheritsFrom("erhic::EventMilou")) {
104  legacymilou=true;
105  }
106 
107  // Run info
108  std::shared_ptr<GenRunInfo> run = std::make_shared<GenRunInfo>();
109  struct GenRunInfo::ToolInfo generator={
110  std::string(generatorname),
111  std::string("unknown version"),
112  std::string("Used generator")
113  };
114  run->tools().push_back(generator);
115  // Can be used to save the name of the control card if known (not usually the case)
116  // struct HepMC3::GenRunInfo::ToolInfo config={cardname,"1.0",std::string("Control cards")};
117 
118  // can be used for customized weight names
119  // currently only DEMP uses weights, named "weight"
120  // We'll need to catch that later but for here just use the default
121  std::vector<std::string> wnames;
122  if (!wnames.size()) wnames.push_back("default");
123  run->set_weight_names(wnames);
124 
125  // cross-section et al are stored as special strings
126  // We don't have incremental information, so attach the full info to the header.
127  // Need to also use HepMC3::GenCrossSection for rivet
128  // Christian Bierlich recommends just using the same for each event
129 
130  double crossSection = 1.0;
131  double crossSectionError = 0.0;
132 
133  // The super set, not all generators supply all of these
134  // could also record accepted_events and attempted_events
135  std::vector <string> RunAttributes = {"crossSection", "crossSectionError", "nEvents", "nTrials" };
136  for ( auto att : RunAttributes ){
137  TObjString* ObjString(nullptr);
138  inFile.GetObject(att.c_str(), ObjString);
139  if (ObjString) {
140  double value = std::atof(ObjString->String());
141  if ( att == "crossSection" ) {
142  // crossSection is in microbarn! Converting to HepMC's pb standard
143  value *=1e6;
144  crossSection = value;
145  }
146  if ( att == "crossSectionError" ){
147  // crossSection is in microbarn! Converting to HepMC's pb standard
148  value *=1e6;
149  crossSectionError = value;
150  }
151  cout << " Adding to the header: " << att << " " << value << endl;
152  run->add_attribute( att, std::make_shared<HepMC3::DoubleAttribute>( value )) ;
153  }
154  }
155 
156  // Construct the output from the input file name,
157  // stripping any leading directory path via
158  // use of the BaseName() method from TSystem.
159  TString outName = gSystem->BaseName(inputFileName.c_str());
160  // Replace the extension
161  outName.Replace(outName.Last('.'), outName.Length(), "");
162  outName.Append(".hepmc");
163 
164  TString outDir(outputDirName);
165  if (!outDir.EndsWith("/")) outDir.Append('/');
166  outName.Prepend(outDir);
167 
168  // Could also use a std::map<HepMC_outtype,std::shared_ptr<HepMC3::Writer> or somesuch
169  // Open the output file.
170  std::shared_ptr<HepMC3::Writer> file;
171  switch ( outtype ) {
173  file = std::make_shared<HepMC3::WriterAsciiHepMC2>(outName.Data(),run);
174  break;
176  file = std::make_shared<HepMC3::WriterAscii>(outName.Data(),run);
177  break;
179  // a tree of (serialized, rootified) GenEvents
180  outName.Append(".root");
181  file = std::make_shared<HepMC3::WriterRootTree>(outName.Data(),run);
182  // file = std::make_shared<HepMC3::WriterRootTree>(outName.Data(),"tree","event",run);
183  break;
185  // a flat collection of N (serialized, rootified) GenEvents
186  outName.Append(".root");
187  file = std::make_shared<HepMC3::WriterRoot>(outName.Data(),run);
188  break;
189  default :
190  cerr << "Unknown HepMC_outtype" << endl;
191  return -1;
192  break; // Unneeded, except sometimes cint gets confused
193  }
194 
195  // Event Loop
196  if (mcTree->GetEntries() < maxEvent || maxEvent < 1) {
197  maxEvent = mcTree->GetEntries();
198  }
199  std::cout <<
200  "/-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-/"
201  << std::endl;
202  std::cout <<
203  "/ Commencing conversion of " << maxEvent << " events."
204  << std::endl;
205  std::cout <<
206  "/-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-/"
207  << std::endl;
208 
209  for (Long64_t i(0); i < maxEvent; i++) {
210  if (i % 10000 == 0 && i != 0) {
211  std::cout << "Processing event " << i << std::endl;
212  }
213  mcTree->GetEntry(i);
214 
215  // Construct new empty HepMC3 event and fill it.
216  // Using GeV and cm (!)
217  GenEvent hepmc3evt( HepMC3::Units::GEV, HepMC3::Units::CM );
218  hepmc3evt.set_event_number(i);
219  hepmc3evt.weights().clear();
220  hepmc3evt.weights().push_back(1.0);
221 
222  // attach cross section in pb
223  GenCrossSectionPtr xsec = std::make_shared<GenCrossSection>();
224  xsec->set_cross_section( crossSection, crossSectionError);
225  hepmc3evt.set_cross_section(xsec);
226 
227  // Go through event-wise variables
228  // Leaves -> particles but also generator-specific variables
229  auto leaves = mcTree->GetListOfLeaves();
230  for ( int l = 0 ; l < leaves->GetEntries(); ++l ){
231  TLeaf* leaf = (TLeaf*) leaves->At(l);
232  TString lname = leaf->GetName();
233  TString c = leaf->GetTypeName();
234  if ( lname.BeginsWith("particles") ) continue;
235  // cout << lname << " " << leaf->GetValue() << endl;
236 
237  // Catch weight
238  if ( lname == "weight"){
239  hepmc3evt.weights().clear();
240  hepmc3evt.weights().push_back( leaf->GetValue() );
241  continue;
242  }
243  // Store generator variables - upconvert types
244  if ( lname.Contains( "char", TString::kIgnoreCase) ) {
245  // This can be a char type or a C string. I'm not aware
246  // of either use case, so don't waste time to differentiate, just ignore
247  continue;
248  }
249  if ( lname.Contains( "long", TString::kIgnoreCase) ) {
250  hepmc3evt.add_attribute(lname.Data(),std::make_shared<HepMC3::LongAttribute>( leaf->GetValue() )) ;
251  continue;
252  }
253  if ( lname.Contains( "int", TString::kIgnoreCase)
254  || lname.Contains( "short", TString::kIgnoreCase) ) {
255  hepmc3evt.add_attribute(lname.Data(),std::make_shared<HepMC3::IntAttribute>( leaf->GetValue() )) ;
256  continue;
257  }
258  if ( lname.Contains( "float", TString::kIgnoreCase)
259  || lname.Contains( "double", TString::kIgnoreCase) ) {
260  hepmc3evt.add_attribute(lname.Data(),std::make_shared<HepMC3::DoubleAttribute>( leaf->GetValue() )) ;
261  continue;
262  }
263  // ignore everything else, e.g. bool
264  } // leaf list
265 
266  // Multiple parents seem to only be in BeAGLE
267  // and somewtimes there seem to be exactly 2 parents, sometimes a range like for daughters.
268  // I cannot differentiate between the two, and for the exact case, it erroneously gives the impression
269  // of a large range, like 17 -- 254 which will wreak havoc on the graph.
270  // "Remedy": pre-burner
271  // - All intermediate non-beam particles have the exchange boson as their mother.
272  // - hadrons and leptons with status 2:
273  // - if they have exactly one parent with status 2 (decay chain),
274  // maintain that parent
275  // - otherwise, they're the start of a decay, treat like a final particle
276  // - hadrons and leptons with status 1:
277  // - if they have exactly one parent with status 2 (decay product),
278  // maintain that parent
279  // - otherwise, they're final, attach to the single final particle vertex
280  // - When the graph gets created, we'll separate and add another dummy node to connect
281  // the boson to via all non-finals
282  // and the finals one as out-going edges
283  // --> Incorrect vertex information (but I don't see it correctly in the original anyway)
284 
285  // Use a special index to refer to the dummy vertex
286  // Should be ushort_max, but keep it flexible
287  auto beagle_final_index = std::numeric_limits< decltype(inEvent->GetTrack(0)->GetParentIndex())>::max();
288 
289  if ( beaglemode ){
290  auto bosonindex=inEvent->ExchangeBoson()->GetIndex();
291  // IMPORTANT! ScatteredLepton() will segfault after we change its lineage!
292  // Last time we can use it.
293  auto scatteredindex = inEvent->ScatteredLepton()->GetIndex();
294 
295  for( unsigned int t=0; t<inEvent->GetNTracks(); ++t) {
296  Particle* inParticle = inEvent->GetTrack(t);
297  auto myindex = inParticle->GetIndex();
298 
299  // special cases first
300  // beam
301  if ( myindex==inEvent->BeamLepton()->GetIndex()
302  || myindex==inEvent->BeamHadron()->GetIndex()
303  ) continue;
304 
305  // Scattered lepton. It may well not be a direct descendant, but we'll stuff that
306  // intermediate history in with the rest. But the beam needs a final lepton daughter
307  if ( myindex==scatteredindex ){
308  inParticle->SetParentIndex( inEvent->BeamLepton()->GetIndex() );
309  inParticle->SetParentIndex1( 0 );
310  inParticle->SetChild1Index( 0 );
311  inParticle->SetChildNIndex( 0 );
312  continue;
313  }
314 
315  // boson
316  if ( myindex==bosonindex ){
317  inParticle->SetChild1Index( 5 );
318  inParticle->SetChildNIndex( inEvent->GetNTracks() );
319  continue;
320  }
321 
322  auto pdg = TDatabasePDG::Instance()->GetParticle( inParticle->Id() );
323  // Note: ROOT's table is outdated and doesn't catch, e.g. Delta baryons
324  switch (inParticle->GetStatus() ){
325  case 2 :
326  // mis-labeled as 2?
327  if ( !pdg ){ // ignore unknown particles (e.g. pomerons, ions)
328  inParticle->SetStatus(12);
329  } else if ( !TString(pdg->ParticleClass()).Contains("Lepton")
330  && !TString(pdg->ParticleClass()).Contains("Baryon")
331  && !TString(pdg->ParticleClass()).Contains("Meson")
332  ){
333  inParticle->SetStatus(12);
334  inParticle->SetParentIndex( bosonindex );
335  inParticle->SetParentIndex1( 0 );
336 
337  inParticle->SetChild1Index( beagle_final_index ); // not needed but logically true
338  inParticle->SetChildNIndex( 0 );
339  } else{
340  // properly labeled as 2. We better have children
341  if ( inParticle->GetChild1Index() == 0 ){
342  std::cout << "Processing event " << i << std::endl;
343  std::cout << "Processing track " << t << " with index " << inParticle->GetIndex() << std::endl;
344  std::cout << "I am a hadron or lepton with status 2, but I do not have children. " << std::endl;
345  return -1;
346  }
347  // We better have exaxctly one parent
348  // Alas, this too does happen
349  if ( inParticle->GetParentIndex1()!=0 ){
350  std::cout << "Processing event " << i << std::endl;
351  std::cout << "Processing track " << t << " with index " << inParticle->GetIndex() << std::endl;
352  std::cout << "Warning: I am a hadron or lepton with status 2, but I have too many parents. " << std::endl;
353  std::cout << "Discarding the older one" << std::endl;
354  // std::cout << inParticle->GetParentIndex() << " " << inParticle->GetParentIndex1() << endl;
355  inParticle->SetParentIndex( std::max ( inParticle->GetParentIndex(), inParticle->GetParentIndex1() ) );
356  inParticle->SetParentIndex1( 0 );
357  }
358  auto mom = inParticle->GetParent();
359  if ( !mom ){
360  std::cout << "Processing event " << i << std::endl;
361  std::cout << "Processing track " << t << " with index " << inParticle->GetIndex() << std::endl;
362  std::cout << "I am a hadron or lepton with status 2, but I have no parents. " << std::endl;
363  return -1;
364  }
365  // status of mother?
366  if ( mom->GetStatus() == 1 ){
367  std::cout << "Processing event " << i << std::endl;
368  std::cout << "Processing track " << t << " with index " << inParticle->GetIndex() << std::endl;
369  std::cout << "I am a hadron or lepton with status 2, but my mother is final. " << std::endl;
370  return -1;
371  }
372  if ( mom->GetStatus() != 2 ){
373  // We're the beginning of a decay, attach to "final" vertex
374  inParticle->SetParentIndex( beagle_final_index );
375  inParticle->SetParentIndex1( 0 );
376  }
377  }
378  break;
379  case 1:
380  {
381  // final particles
382  auto mom = inParticle->GetParent();
383  if ( mom ){
384  // status of mother?
385  if ( mom->GetStatus() == 2 ){
386  // do nothing, we keep this mother as ours
387  inParticle->SetChild1Index( 0 );
388  inParticle->SetChildNIndex( 0 );
389  break;
390  }
391  }
392  // default behavior for finals
393  inParticle->SetParentIndex( beagle_final_index );
394  inParticle->SetParentIndex1( 0 );
395 
396  inParticle->SetChild1Index( 0 );
397  inParticle->SetChildNIndex( 0 );
398  break;
399  }
400  default :
401  // everything else
402  inParticle->SetParentIndex( bosonindex );
403  inParticle->SetParentIndex1( 0 );
404 
405  inParticle->SetChild1Index( beagle_final_index ); // not needed but logically true
406  inParticle->SetChildNIndex( 0 );
407  break;
408  }
409  }
410  } // if ( beaglemode )
411 
412  if ( legacymilou && inEvent->BeamLepton()->GetChild1Index()==0 ){
413  if ( !milouwarn ){
414  cout << "Warning: Trying to repair legay Milou's parentage issues." << endl;
415  cout << endl;
416  milouwarn=true;
417  }
418 
419  // e
420  inEvent->GetTrack(1-1)->SetChild1Index(3);
421  inEvent->GetTrack(1-1)->SetChildNIndex(4);
422 
423  // p
424  inEvent->GetTrack(2-1)->SetChild1Index(6);
425  inEvent->GetTrack(2-1)->SetChildNIndex(0);
426 
427  // e'
428  inEvent->GetTrack(3-1)->SetParentIndex(1);
429  inEvent->GetTrack(3-1)->SetChild1Index(0);
430  inEvent->GetTrack(3-1)->SetChildNIndex(0);
431 
432  // gamma*
433  inEvent->GetTrack(4-1)->SetParentIndex(1);
434  inEvent->GetTrack(4-1)->SetChild1Index(5);
435  inEvent->GetTrack(4-1)->SetChildNIndex(0);
436 
437  // gamma
438  inEvent->GetTrack(5-1)->SetParentIndex(4);
439  inEvent->GetTrack(5-1)->SetChild1Index(0);
440  inEvent->GetTrack(5-1)->SetChildNIndex(0);
441 
442  // p'
443  inEvent->GetTrack(6-1)->SetParentIndex(2);
444  inEvent->GetTrack(6-1)->SetChild1Index(0);
445  inEvent->GetTrack(6-1)->SetChildNIndex(0);
446 
447  // ISR
448  if (inEvent->GetTrack(7-1) ){
449  inEvent->GetTrack(7-1)->SetParentIndex(0);
450  inEvent->GetTrack(7-1)->SetChild1Index(0);
451  inEvent->GetTrack(7-1)->SetChildNIndex(0);
452  }
453  }
454 
455  // First, fix sloppily implemented mother-daughter relations
456  // Not done for BeAGLE, because of the special vertex
457  if ( !beaglemode ){
458  for( unsigned int t=0; t<inEvent->GetNTracks(); ++t) {
459  const Particle* inParticle = inEvent->GetTrack(t);
460 
461  // Do my children know me?
462  auto myindex = inParticle->GetIndex();
463  // std::cout << "Processing track " << t << " with index " << myindex << std::endl;
464  auto c1 = inParticle->GetChild1Index();
465  auto cN = inParticle->GetChildNIndex();
466  if ( cN==0 ) cN =c1;
467  if ( c1>cN ) std::swap(c1,cN);
468  if ( c1>0 ) {
469 
470  //In a small number of Djangoh events, the particle list will be incomplete.
471  //A particle will have a child which is not included in the particle list.
472  bool djangohproblem = false;
473 
474  for ( UShort_t c = c1; c<=cN; ++c ){ // sigh. index starts at 1, tracks at 0;
475  Particle* child = inEvent->GetTrack(c-1);
476  if ( !child ) {
477  cerr << "Trying to access a non-existant child" << endl;
478  cerr << "Event is " << i << " Problem index is " << c << endl;
479  cerr << "If this is not a djangoh file, please contact the eic-smear developers"<<endl;
480  djangohproblem = true;
481  break;
482  }
483 
484  // std::cout << " Processing child with index " << child->GetIndex() << std::endl;
485  auto p1 = child->GetParentIndex();
486  auto pN = child->GetParentIndex1();
487  if ( p1>pN ) std::swap(p1,pN);
488  if ( p1==0 && pN==0 ){ // child erroneously believes to be motherless
489  child->SetParentIndex( myindex );
490  } else if ( p1==0 ) { // We are the only parent, is it correctly assigned?
491  if ( pN != myindex ){
492  // Nothing we can do, e.g. pythia allows multiple parenthood but lacks a way to describe that, see:
493  // 12 12 2101 5 18 31
494  // ...
495  // 16 11 2 10 18 31
496  // ...
497  // 26 1 -211 12 0 0
498  // 27 1 211 16 0 0
499  // cerr << "My child thinks its mother is " << pN << ", but it should be " << myindex << endl;
500  // return -1;
501  }
502  } else {
503  // If multiple parents come from non-BeAGLE MC's revisit
504  cout << "Found more than one parent in a non-BeAGLE file. Please contact the authors." << endl;
505  return -1;
506  // We have more than one parent, are they correct?
507  // This would be the logic if p1 and pN _span_
508  // if ( myindex < p1 || myindex > pN ){
509  // std::cout << "Processing event " << i << std::endl;
510  // std::cout << "Processing track " << t << " with index " << myindex << std::endl;
511  // std::cout << " Processing child with index " << child->GetIndex() << std::endl;
512  // cerr << "My child thinks its mothers range between " << p1 << " and " << pN
513  // << ", but I am " << myindex << endl;
514  // // return -1;
515  // }
516  // Instead, it seems that BeAGLE (mostly?) assumes this to mean
517  // exactly two parents, usually far apart in index
518  // if ( myindex != p1 && myindex != pN ){
519  // // Problematic situation in BeAGLE:
520  // // I S PID P1 P2 D1 D2
521  // // ==========================================================
522  // // 17 18 2112 0 0 260 261
523  // // ...
524  // // 254 19 111 41 244 260 261
525  // // 255 2 2212 41 244 260 261
526  // // ...
527  // // 260 16 2112 17 254 0 0
528  // // 261 16 2212 17 254 0 0
529  // }
530  }
531  }
532  if(djangohproblem) continue;
533  }
534  // Do my parents acknowledge me?
535  auto p1 = inParticle->GetParentIndex();
536  auto pN = inParticle->GetParentIndex1();
537  if ( p1>pN ) std::swap(p1,pN);
538  if ( p1==0 ) p1 =pN;
539  // Do all my parents acknowledge me?
540  if (pN > 0){
541  for ( unsigned int p = p1; p<=pN ; ++p ){
542  Particle* parent = inEvent->GetTrack(p-1);
543  auto pc1 = parent->GetChild1Index();
544  auto pcN = parent->GetChildNIndex();
545  if ( pc1>pcN ) std::swap(pc1,pcN);
546  if ( pc1 > myindex ){
547  // cout << "hello1" << endl;
548  parent->SetChild1Index( myindex );
549  }
550  if ( pcN < myindex ){
551  // cout << "hello2" << endl;
552  parent->SetChildNIndex( myindex );
553  }
554  }
555  }
556  }
557  } // graph repair for !beaglemode
558 
559  // Perform consistency checks and collect particles
560  std::vector<GenParticlePtr> hepevt_particles;
561  hepevt_particles.reserve( inEvent->GetNTracks() );
562  for( unsigned int t=0; t<inEvent->GetNTracks(); ++t) {
563  const Particle* inParticle = inEvent->GetTrack(t);
564  // Particles with status 1 cannot have children
565  auto status = inParticle->GetStatus();
566  if ( status==1 ){
567  if (inParticle->GetNChildren() != 0 ){
568  cout << "Status is 1 but we have children?" << endl;
569  inParticle->Print();
570  }
571  }
572 
573  // // All child-less particles should have a "safe" status (like 21), best would be 1
574  // if (inParticle->GetNChildren() == 0 ){
575  // if ( status !=1 && status !=21 ){
576  // cerr << "Status is " << status << " but we have no children?" << endl;
577  // inParticle->Print();
578  // }
579  // }
580 
581  // // Mother-less particles should be the beam only
582  // Alas, that's not the case :-/
583  // if ( t>1 && inParticle->GetParentIndex()==0 && inParticle->GetParentIndex1() ==0 ){
584  // cout << "Event: " << i << " -- We have no mother" << endl;
585  // inParticle->Print();
586  // }
587 
588  FourVector pv = FourVector( inParticle->GetPx(), inParticle->GetPy(), inParticle->GetPz(),inParticle->GetE() );
589  int statusHepMC = inParticle->GetStatus();
590  // We should use only 1 (final), 2 (decayed hadron or lepton), 4 (beam), and >10, <=200 (anything else)
591  // This may need to be decided on a generator-by-generator basis
592  // We can assume final particles already have status 1, because that's
593  // what EventMC::FinalState uses (and it's not overridden in existing classes)
594 
595  // Catch decayed leptons and hadrons
596  // doesn't work for BeAGLE
597  if ( ! beaglemode && t>3 ){ // Ignore the beam
598  if (inParticle->GetNChildren() != 0 ){ // ignore final particles
599  auto pdg = TDatabasePDG::Instance()->GetParticle( inParticle->Id() );
600  if ( pdg ){ // ignore unknown particles (e.g. pomerons, ions)
601  if ( TString(pdg->ParticleClass()).Contains("Lepton")
602  || TString(pdg->ParticleClass()).Contains("Baryon")
603  || TString(pdg->ParticleClass()).Contains("Meson")
604  ){
605  // Now our status should be 2!
606  // cout << statusHepMC << endl;
607  // inParticle->Print();
608  statusHepMC = 2;
609  }
610  }
611  }
612  }
613 
614  // // catch DJANGOH trying to assign 4 to non beams
615  // if ( t>3 && (statusHepMC == 4 ) ){
616  // cout << "Naughty Djangoh" << endl;
617  // statusHepMC=14;
618  // }
619 
620  // force everything else to be legal
621  if ( statusHepMC != 1 && statusHepMC != 2 && statusHepMC != 4 ){
622  while ( statusHepMC <=10 ) statusHepMC+=10;
623  while ( statusHepMC >200 ) statusHepMC-=10;
624  }
625 
626  //In the Pythia 6 convention, status=4 indicates a particle which could
627  //have decayed but did not within the allowed volume around the vertex.
628  //We adjust these particles to have status=1 in the HepMC file to avoid
629  //confusion with the beam particles.
630  if( statusHepMC == 4) statusHepMC = 1;
631 
632  // Create GenParticle
633  hepevt_particles.push_back( std::make_shared<GenParticle>( pv, inParticle->Id(), statusHepMC ));
634  hepevt_particles.back()->set_generated_mass( inParticle->GetM() );
635 
636  }
637 
638  // Build the event
639  // beam particles
640  // --------------
641  // Default is 1 = e-, 2 = hadron, 3 = scattered e-, 4 = exchange boson
642  // But this can (and does) differ, especially for the scattered lepton
643  // As always, be aware of Fortran starting to count at 1
644  // Vertex: We don't keep track of time
645  auto lepton=inEvent->BeamLepton();
646  int index_lepton = lepton->GetIndex();
647  if ( index_lepton !=1 ) std::cout << "Warning: Found BeamLepton at " << index_lepton << endl;
648  auto hep_lepton = hepevt_particles.at( index_lepton-1);
649  hep_lepton->set_status(4);
650 
651  auto boson=inEvent->ExchangeBoson();
652  int index_boson = boson->GetIndex();
653  // This happens in Sartre who puts the boson at 3
654  // if ( index_boson !=4 ) std::cout << "Warning: Found ExchangeBoson at " << index_boson << endl;
655  // if ( boson->GetParentIndex() != index_lepton && boson->GetParentIndex1() != index_lepton ){
656  // // This is common for Sartre, and any others that treat the boson like the beam
657  // std::cout << "Warning: ExchangeBoson doesn't recognize the beam as its mother " << endl;
658  // }
659  auto hep_boson = hepevt_particles.at( index_boson-1);
660  // if needed / desired, could force hep_boson->set_status(4);
661 
662  GenVertexPtr v_lepton = std::make_shared<GenVertex>();
663  v_lepton->add_particle_in (hep_lepton);
664  v_lepton->add_particle_out (hep_boson);
665  hepmc3evt.add_vertex(v_lepton);
666 
667  auto hadron=inEvent->BeamHadron();
668  int index_hadron = hadron->GetIndex();
669  if ( index_hadron !=2 ) std::cout << "Warning: Found BeamHadron at " << index_hadron << endl;
670  auto hep_hadron = hepevt_particles.at( index_hadron-1);
671  hep_hadron->set_status(4);
672  GenVertexPtr v_hadron = std::make_shared<GenVertex>();
673 
674  v_hadron->add_particle_in (hep_hadron);
675  hepmc3evt.add_vertex(v_hadron);
676 
677  // For Beagle, use
678  //
679  // e e'
680  // \v1__/ i1 f1
681  // \_gamma / \ /
682  // \ _v2_/__i2__ v3__f2
683  // / \ / \ (*)
684  // proton \iN / \fN
685  //
686  // where i1, .., iN are intermediate (!=1) and f1,..fN are final
687  // v2 == v_hadron, v3 == v_beagle_final
688 
689  // Addendum: BeAGLE does support hadron/lepton decay. Attach the root to v3,
690  // and keep their children, e.g.:
691  //
692  // v2_/__i2__ v3__J/psi__e
693  // \ \ (*)
694  // \fN e
695  //
696  // (But also keep decay chains)
697  // (*) comment lines ending in "\" generate compiler warnings
698 
699  // Dummy to act as a catchall for intermediary particles in beagle
700  GenVertexPtr v_beagle_final = std::make_shared<GenVertex>();
701  if ( beaglemode ){
702  v_hadron->add_particle_in(hep_boson);
703  hepmc3evt.add_vertex( v_beagle_final );
704  // We don't have a connection yet, so in the pathological case
705  // that there are no non-final particles at all, this vertex floats free.
706  // That's too unlikely to occur to build in a fail-safe now
707  }
708 
709  // Now work our way through the remaining particles
710  // - Attach each particle that has a mother to their end vertex
711  // ---> Create / overwrite production vertex in the process.
712  // If it's inconsistent, there's not much we can do
713  // - attach motherless particles to the exchange boson
714  // ---> In that case, leave the production vertex location in peace
715  // ---> Also note that ISR photons are motherless and thus
716  // get attached to the exchange boson as well
717  // Topological order should just translate to the fact that
718  // children always have a higher index than their parents
719 
720  // Note: Multiple parents would wreak havoc here - have to handle BeAGLE differently
721  for( unsigned int t=0; t<inEvent->GetNTracks(); ++t) {
722  const Particle* inParticle = inEvent->GetTrack(t);
723 
724  // Skip what we already have
725  int index = inParticle->GetIndex();
726  if ( index==index_lepton || index==index_boson || index==index_hadron) continue;
727  auto hep_in = hepevt_particles.at( index-1);
728  // auto hep_mom = hep_boson;
729  auto hep_mom = hep_hadron;
730  int momindex = inParticle->GetParentIndex();
731  auto statusHepMC = inParticle->GetStatus();
732 
733  //For Djangoh events which fail to hadronize, shift the parent of the
734  //final-state parton from the incoming electron beam to th hadron beam
735  if (branchClass->InheritsFrom("erhic::EventDjangoh")){
736  if( statusHepMC==1 && momindex==1 &&
737  (abs(inParticle->Id())==1 || abs(inParticle->Id())==2 || abs(inParticle->Id())==3 ||
738  abs(inParticle->Id())==90 || inParticle->Id()==91 || inParticle->Id()==92) ){
739  momindex+=1;
740  }
741  }
742 
743  // suppress all the intermediate nucleons
744  // this may be worth doing anyway just to reduce filesize
745  if ( beaglemode && statusHepMC==3 ) continue;
746  if ( beaglemode && statusHepMC==14 ) continue;
747  if ( beaglemode && statusHepMC==18 ) continue;
748  if ( beaglemode && statusHepMC==12 ) continue;
749  // This is purely for legibility, these particles should stay!
750  // note: 80000 are lighter ions, without specification
751  // if ( beaglemode && statusHepMC==1 && momindex == beagle_final_index
752  // && ( hep_in->pid() == 2112 || hep_in->pid() == 2212 || hep_in->pid() == 80000 ) ) continue;
753 
754  // beagle finals
755  if ( momindex == beagle_final_index ){
756  v_beagle_final->add_particle_out(hep_in);
757  continue;
758  }
759 
760  // beagle intermediates
761  // out will be handled, but need to attach as incoming
762  if ( beaglemode && statusHepMC!=1 && statusHepMC!=2 ){
763  v_beagle_final->add_particle_in(hep_in);
764  }
765 
766  // Mother?
767  if ( momindex > 0 ){
768  hep_mom = hepevt_particles.at( momindex-1);
769  }
770 
771  // Does mom have an end vertex yet?
772  auto momend = hep_mom->end_vertex();
773  if (!momend) {
774  momend = std::make_shared<GenVertex>();
775  momend->add_particle_in(hep_mom);
776  hepmc3evt.add_vertex(momend);
777  }
778 
779  momend->add_particle_out(hep_in);
780 
781  // update prod vertex?
782  if ( momindex > 1){
783  auto vnew = inParticle->GetVertex();
784  momend->set_position( FourVector( vnew.x(), vnew.y(), vnew.z(), 0));
785  }
786  // file->write_event(hepmc3evt);
787  }
788 
789  // Done! Write the event.
790  file->write_event(hepmc3evt);
791  // There's a bunch of cleanup one should do now, with all the dynamical
792  // vertices and particles. BUT shared_ptr should take care of that. Revisit if there are memory leaks.
793  // break;
794 
795  } // event loop
796 
797  file->close();
798 
799  Long64_t result = 0;
800 
801  return result;
802 }
803 
804 
808 Long64_t TreeToHepMC(const std::string& inputFileName,
809  const std::string& outputDirName,
810  Long64_t maxEvent,
811  const bool createHepMC2){
812  if ( createHepMC2 ) {
813  cout << "Warning, this interface is deprecated, please use:" << endl;
814  cout << "TreeToHepMC(\""<< inputFileName<< "\",\""<< outputDirName
815  << "\","<< maxEvent << ","
816  << "erhic::HepMC_outtype::HepMC2)"
817  << endl;
818  return TreeToHepMC(inputFileName,outputDirName,maxEvent,erhic::HepMC_outtype::HepMC2);
819  } else {
820  cout << "Warning, this interface is deprecated, please use:" << endl;
821  cout << "TreeToHepMC(\""<< inputFileName<< "\",\""<< outputDirName
822  << "\","<< maxEvent << ","
823  << "erhic::HepMC_outtype::HepMC3)"
824  << endl;
825  return TreeToHepMC(inputFileName,outputDirName,maxEvent,erhic::HepMC_outtype::HepMC3);
826  }
827 
828 
829  // outtype == erhic::HepMC_outtype::HepMC3 ) {
830  // return TreeToHepMC(inputFileName, outputDirName,maxEvent,false);
831  // }
832  // if ( outtype == erhic::HepMC_outtype::HepMC2 ) {
833  // return TreeToHepMC(inputFileName, outputDirName,maxEvent,true);
834  // }
835  return -1;
836 }
837 
838