Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2024-06-29 07:05:52

0001 /**
0002  \file
0003  Defines the main TreeToHepMC function.
0004 
0005  \author    Kolja Kauder
0006  \date      2021-07-07
0007  \copyright 2021 Brookhaven National Lab
0008  */
0009 
0010 #include <string>
0011 #include <iostream>
0012 
0013 #include <TString.h>
0014 #include <TSystem.h>
0015 #include <TBranch.h>
0016 #include <TLeaf.h>
0017 #include <TDatabasePDG.h>
0018 
0019 #include "eicsmear/erhic/Forester.h"
0020 #include "eicsmear/erhic/File.h"
0021 
0022 #include "HepMC3/GenEvent.h"
0023 #include "HepMC3/GenVertex.h"
0024 #include "HepMC3/GenParticle.h"
0025 #include "HepMC3/GenCrossSection.h"
0026 #include "HepMC3/Attribute.h"
0027 #include "HepMC3/Version.h"
0028 #include "HepMC3/WriterAscii.h"
0029 #include "HepMC3/WriterAsciiHepMC2.h"
0030 #include "HepMC3/WriterRoot.h"
0031 #include "HepMC3/WriterRootTree.h"
0032 
0033 #include <map>
0034 
0035 using std::cout;
0036 using std::cerr;
0037 using std::endl;
0038 
0039 using HepMC3::FourVector;
0040 using HepMC3::GenRunInfo;
0041 using HepMC3::GenEvent;
0042 using HepMC3::GenParticle;
0043 using HepMC3::GenParticlePtr;
0044 using HepMC3::GenVertex;
0045 using HepMC3::GenVertexPtr;
0046 using HepMC3::GenCrossSection;
0047 using HepMC3::GenCrossSectionPtr;
0048 
0049 // see include/eicsmear/functions.h for declaration and default values
0050 
0051 /**
0052  This function converts our tree format to HepMC3
0053  It would be better to skip the ROOT step, but that
0054  would require a lot of duplication and/or refactorization
0055  */
0056 Long64_t TreeToHepMC(const std::string& inputFileName,
0057              const std::string& outputDirName,
0058              Long64_t maxEvent,
0059              const erhic::HepMC_outtype outtype) {
0060   
0061   // Make sure this is a root file, 
0062   if ( !TString(inputFileName).EndsWith(".root", TString::kIgnoreCase) ){
0063     cerr << "Warning: " << inputFileName << " does not end with .root" << endl;
0064   }
0065   
0066   // Open the input file and get the Monte Carlo tree from it.
0067   // Complain and quit if we don't find the file or the tree.
0068   TFile inFile(inputFileName.c_str(), "READ");
0069   if (!inFile.IsOpen()) {
0070     std::cerr << "Unable to open " << inputFileName << std::endl;
0071   }  // if
0072   TTree* mcTree(NULL);
0073   // TODO: Extend to smeared trees
0074   inFile.GetObject("EICTree", mcTree);
0075   if (!mcTree) {
0076     std::cerr << "Unable to find EICTree in " << inputFileName << std::endl;
0077     return 1;
0078   }
0079   erhic::EventMC* inEvent(NULL);
0080   mcTree->SetBranchAddress("event",&inEvent);
0081 
0082   // Get generator name
0083   TClass* branchClass = TClass::GetClass(mcTree->GetBranch("event")->GetClassName());
0084   TString generatorname = branchClass->GetName();
0085   if (branchClass->InheritsFrom("erhic::EventDis")) {
0086     generatorname.ReplaceAll("erhic::Event","");
0087   } else {
0088     cerr << branchClass->GetName() << " is not supported." << endl;
0089     return -1;
0090   }  // if
0091 
0092   // BeAGLE is currently unfixable; using a kludge to salvage what we can
0093   bool beaglemode=false;
0094   if (branchClass->InheritsFrom("erhic::EventBeagle")) {
0095     cout << "Warning: BeAGLE support is rudimentary. Can't fix mother-daughter structure." << endl;
0096     cout << endl;
0097     beaglemode=true;
0098   }
0099 
0100   // Older Milou files need special treatment
0101   bool legacymilou=false;
0102   bool milouwarn=false; // Need to enter event loop to determine, but warn only once
0103   if (branchClass->InheritsFrom("erhic::EventMilou")) {
0104     legacymilou=true;
0105   }
0106 
0107   // Run info
0108   std::shared_ptr<GenRunInfo> run = std::make_shared<GenRunInfo>();
0109   struct GenRunInfo::ToolInfo generator={
0110     std::string(generatorname),
0111     std::string("unknown version"),
0112     std::string("Used generator")
0113   };
0114   run->tools().push_back(generator);
0115   // Can be used to save the name of the control card if known (not usually the case)
0116   // struct HepMC3::GenRunInfo::ToolInfo config={cardname,"1.0",std::string("Control cards")};
0117 
0118   // can be used for customized weight names
0119   // currently only DEMP uses weights, named "weight"
0120   // We'll need to catch that later but for here just use the default
0121   std::vector<std::string> wnames;
0122   if (!wnames.size()) wnames.push_back("default");
0123   run->set_weight_names(wnames);
0124   
0125   // cross-section et al are stored as special strings
0126   // We don't have incremental information, so attach the full info to the header.
0127   // Need to also use HepMC3::GenCrossSection for rivet
0128   // Christian Bierlich recommends just using the same for each event
0129 
0130   double crossSection = 1.0;
0131   double crossSectionError = 0.0;
0132   
0133   // The super set, not all generators supply all of these
0134   // could also record  accepted_events and attempted_events
0135   std::vector <string> RunAttributes = {"crossSection", "crossSectionError", "nEvents", "nTrials" };
0136   for ( auto att : RunAttributes ){
0137     TObjString* ObjString(nullptr);
0138     inFile.GetObject(att.c_str(), ObjString);
0139     if (ObjString) {
0140       double value = std::atof(ObjString->String());
0141       if ( att == "crossSection" ) {
0142     // crossSection is in microbarn! Converting to HepMC's pb standard
0143     value *=1e6;
0144     crossSection = value;
0145       }
0146       if ( att == "crossSectionError" ){
0147     // crossSection is in microbarn! Converting to HepMC's pb standard
0148     value *=1e6;
0149     crossSectionError = value;
0150       }
0151       cout << " Adding to the header: " << att << "  " << value << endl;
0152       run->add_attribute( att, std::make_shared<HepMC3::DoubleAttribute>( value )) ;
0153     }
0154   }
0155 
0156   // Construct the output from the input file name,
0157   // stripping any leading directory path via
0158   // use of the BaseName() method from TSystem.
0159   TString outName = gSystem->BaseName(inputFileName.c_str());
0160   // Replace the extension
0161   outName.Replace(outName.Last('.'), outName.Length(), "");
0162   outName.Append(".hepmc");
0163 
0164   TString outDir(outputDirName);
0165   if (!outDir.EndsWith("/")) outDir.Append('/');
0166   outName.Prepend(outDir);
0167 
0168   // Could also use a std::map<HepMC_outtype,std::shared_ptr<HepMC3::Writer> or somesuch
0169   // Open the output file.
0170   std::shared_ptr<HepMC3::Writer> file;
0171   switch ( outtype ) {
0172   case erhic::HepMC_outtype::HepMC2 :
0173     file = std::make_shared<HepMC3::WriterAsciiHepMC2>(outName.Data(),run);
0174     break;
0175   case erhic::HepMC_outtype::HepMC3 :
0176     file = std::make_shared<HepMC3::WriterAscii>(outName.Data(),run);
0177     break;
0178   case erhic::HepMC_outtype::RootTree :
0179     // a tree of (serialized, rootified) GenEvents
0180     outName.Append(".root");
0181     file = std::make_shared<HepMC3::WriterRootTree>(outName.Data(),run);
0182     // file = std::make_shared<HepMC3::WriterRootTree>(outName.Data(),"tree","event",run);
0183     break;
0184   case erhic::HepMC_outtype::Root :
0185     // a flat collection of N (serialized, rootified) GenEvents
0186     outName.Append(".root");
0187     file = std::make_shared<HepMC3::WriterRoot>(outName.Data(),run);
0188     break;
0189   default :
0190     cerr << "Unknown HepMC_outtype" << endl;
0191     return -1;
0192     break; // Unneeded, except sometimes cint gets confused
0193   }
0194   
0195   // Event Loop
0196   if (mcTree->GetEntries() < maxEvent || maxEvent < 1) {
0197     maxEvent = mcTree->GetEntries();
0198   } 
0199   std::cout <<
0200   "/-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-/"
0201   << std::endl;
0202   std::cout <<
0203   "/  Commencing conversion of " << maxEvent << " events."
0204   << std::endl;
0205   std::cout <<
0206   "/-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-/"
0207   << std::endl;
0208 
0209   for (Long64_t i(0); i < maxEvent; i++) {
0210     if (i % 10000 == 0 && i != 0) {
0211       std::cout << "Processing event " << i << std::endl;
0212     } 
0213     mcTree->GetEntry(i);
0214     
0215     // Construct new empty HepMC3 event and fill it.
0216     // Using GeV and cm (!)
0217     GenEvent hepmc3evt( HepMC3::Units::GEV, HepMC3::Units::CM );
0218     hepmc3evt.set_event_number(i);
0219     hepmc3evt.weights().clear();
0220     hepmc3evt.weights().push_back(1.0);
0221 
0222     // attach cross section in pb
0223     GenCrossSectionPtr xsec = std::make_shared<GenCrossSection>();
0224     xsec->set_cross_section( crossSection, crossSectionError);
0225     hepmc3evt.set_cross_section(xsec);
0226 
0227     // Go through event-wise variables
0228     // Leaves -> particles but also generator-specific variables
0229     auto leaves = mcTree->GetListOfLeaves();
0230     for ( int l = 0 ; l < leaves->GetEntries(); ++l ){
0231       TLeaf* leaf = (TLeaf*) leaves->At(l);
0232       TString lname = leaf->GetName();
0233       TString c = leaf->GetTypeName();
0234       if ( lname.BeginsWith("particles") ) continue;
0235       // cout << lname << "  " << leaf->GetValue() << endl;
0236 
0237       // Catch weight
0238       if ( lname == "weight"){
0239     hepmc3evt.weights().clear();
0240     hepmc3evt.weights().push_back( leaf->GetValue() );
0241     continue;
0242       }
0243       // Store generator variables - upconvert types
0244       if ( lname.Contains( "char", TString::kIgnoreCase) ) {
0245     // This can be a char type or a C string. I'm not aware
0246     // of either use case, so don't waste time to differentiate, just ignore
0247     continue;
0248       }
0249       if ( lname.Contains( "long", TString::kIgnoreCase) ) {
0250     hepmc3evt.add_attribute(lname.Data(),std::make_shared<HepMC3::LongAttribute>( leaf->GetValue() )) ;
0251     continue;
0252       }
0253       if ( lname.Contains( "int", TString::kIgnoreCase) 
0254        || lname.Contains( "short", TString::kIgnoreCase) ) {
0255     hepmc3evt.add_attribute(lname.Data(),std::make_shared<HepMC3::IntAttribute>( leaf->GetValue() )) ;
0256     continue;
0257       }
0258       if ( lname.Contains( "float", TString::kIgnoreCase)
0259        || lname.Contains( "double", TString::kIgnoreCase) ) {
0260     hepmc3evt.add_attribute(lname.Data(),std::make_shared<HepMC3::DoubleAttribute>( leaf->GetValue() )) ;
0261     continue;
0262       }
0263       // ignore everything else, e.g. bool      
0264     } // leaf list
0265 
0266     // Multiple parents seem to only be in BeAGLE
0267     // and somewtimes there seem to be exactly 2 parents, sometimes a range like for daughters.
0268     // I cannot differentiate between the two, and for the exact case, it erroneously gives the impression
0269     // of a large range, like 17 -- 254 which will wreak havoc on the graph.
0270     // "Remedy": pre-burner
0271     // - All intermediate non-beam particles have the exchange boson as their mother.
0272     // - hadrons and leptons with status 2:
0273     //   - if they have exactly one parent with status 2 (decay chain),
0274     //     maintain that parent
0275     //   - otherwise, they're the start of a decay, treat like a final particle
0276     // - hadrons and leptons with status 1:
0277     //   - if they have exactly one parent with status 2 (decay product),
0278     //     maintain that parent
0279     //   - otherwise, they're final, attach to the single final particle vertex
0280     // - When the graph gets created, we'll separate and add another dummy node to connect
0281     //   the boson to via all non-finals
0282     //   and the finals one as out-going edges
0283     // --> Incorrect vertex information (but I don't see it correctly in the original anyway)
0284 
0285     // Use a special index to refer to the dummy vertex
0286     // Should be ushort_max, but keep it flexible
0287     auto beagle_final_index = std::numeric_limits< decltype(inEvent->GetTrack(0)->GetParentIndex())>::max();
0288     
0289     if ( beaglemode ){
0290       auto bosonindex=inEvent->ExchangeBoson()->GetIndex();
0291       // IMPORTANT! ScatteredLepton() will segfault after we change its lineage!
0292       // Last time we can use it.
0293       auto scatteredindex = inEvent->ScatteredLepton()->GetIndex();
0294       
0295       for( unsigned int t=0; t<inEvent->GetNTracks(); ++t) {
0296     Particle* inParticle = inEvent->GetTrack(t);
0297     auto myindex = inParticle->GetIndex();
0298     
0299     // special cases first
0300     // beam
0301     if ( myindex==inEvent->BeamLepton()->GetIndex()
0302          || myindex==inEvent->BeamHadron()->GetIndex()
0303          ) continue;
0304 
0305     // Scattered lepton. It may well not be a direct descendant, but we'll stuff that
0306     // intermediate history in with the rest. But the beam needs a final lepton daughter
0307     if ( myindex==scatteredindex ){
0308       inParticle->SetParentIndex( inEvent->BeamLepton()->GetIndex() );
0309       inParticle->SetParentIndex1( 0 );
0310       inParticle->SetChild1Index( 0 );
0311       inParticle->SetChildNIndex( 0 );
0312       continue;
0313     }
0314     
0315     // boson
0316     if ( myindex==bosonindex ){
0317       inParticle->SetChild1Index( 5 );
0318       inParticle->SetChildNIndex( inEvent->GetNTracks() );
0319       continue;
0320     }
0321     
0322     auto pdg = TDatabasePDG::Instance()->GetParticle( inParticle->Id() );
0323     // Note: ROOT's table is outdated and doesn't catch, e.g. Delta baryons 
0324     switch (inParticle->GetStatus() ){
0325     case 2 :
0326       // mis-labeled as 2?
0327       if ( !pdg ){ // ignore unknown particles (e.g. pomerons, ions)
0328         inParticle->SetStatus(12);
0329       } else if ( !TString(pdg->ParticleClass()).Contains("Lepton")
0330               && !TString(pdg->ParticleClass()).Contains("Baryon")
0331               && !TString(pdg->ParticleClass()).Contains("Meson")
0332               ){
0333         inParticle->SetStatus(12);
0334         inParticle->SetParentIndex( bosonindex );
0335         inParticle->SetParentIndex1( 0 );
0336         
0337         inParticle->SetChild1Index( beagle_final_index ); // not needed but logically true
0338         inParticle->SetChildNIndex( 0 );   
0339       } else{
0340         // properly labeled as 2. We better have children
0341         if ( inParticle->GetChild1Index() == 0 ){
0342           std::cout << "Processing event " << i << std::endl;
0343           std::cout << "Processing track " << t << " with index " << inParticle->GetIndex() << std::endl;
0344           std::cout << "I am a hadron or lepton with status 2, but I do not have children. "  << std::endl;
0345           return -1;
0346         }
0347         // We better have exaxctly one parent
0348         // Alas, this too does happen
0349         if ( inParticle->GetParentIndex1()!=0 ){
0350           std::cout << "Processing event " << i << std::endl;
0351           std::cout << "Processing track " << t << " with index " << inParticle->GetIndex() << std::endl;
0352           std::cout << "Warning: I am a hadron or lepton with status 2, but I have too many parents. "  << std::endl;
0353           std::cout << "Discarding the older one"  << std::endl;
0354           // std::cout << inParticle->GetParentIndex() << "  " << inParticle->GetParentIndex1() << endl;
0355           inParticle->SetParentIndex( std::max ( inParticle->GetParentIndex(), inParticle->GetParentIndex1() ) );
0356           inParticle->SetParentIndex1( 0 );
0357         }
0358         auto mom = inParticle->GetParent();
0359         if ( !mom ){
0360           std::cout << "Processing event " << i << std::endl;
0361           std::cout << "Processing track " << t << " with index " << inParticle->GetIndex() << std::endl;
0362           std::cout << "I am a hadron or lepton with status 2, but I have no parents. "  << std::endl;
0363           return -1;
0364         }
0365         // status of mother?
0366         if ( mom->GetStatus() == 1 ){
0367           std::cout << "Processing event " << i << std::endl;
0368           std::cout << "Processing track " << t << " with index " << inParticle->GetIndex() << std::endl;
0369           std::cout << "I am a hadron or lepton with status 2, but my mother is final. "  << std::endl;
0370           return -1;
0371         }
0372         if ( mom->GetStatus() != 2 ){
0373           // We're the beginning of a decay, attach to "final" vertex
0374           inParticle->SetParentIndex( beagle_final_index );
0375           inParticle->SetParentIndex1( 0 );
0376         }       
0377       }
0378       break;
0379     case 1:
0380       {
0381         // final particles
0382         auto mom = inParticle->GetParent();
0383         if ( mom ){
0384           // status of mother?
0385           if ( mom->GetStatus() == 2 ){
0386         // do nothing, we keep this mother as ours
0387         inParticle->SetChild1Index( 0 );
0388         inParticle->SetChildNIndex( 0 );
0389         break;
0390           }
0391         }
0392         // default behavior for finals
0393         inParticle->SetParentIndex( beagle_final_index );
0394         inParticle->SetParentIndex1( 0 );
0395         
0396         inParticle->SetChild1Index( 0 );
0397         inParticle->SetChildNIndex( 0 );
0398         break;
0399       }
0400     default : 
0401       // everything else
0402       inParticle->SetParentIndex( bosonindex );
0403       inParticle->SetParentIndex1( 0 );
0404       
0405       inParticle->SetChild1Index( beagle_final_index ); // not needed but logically true
0406       inParticle->SetChildNIndex( 0 );
0407       break;
0408     }
0409       }
0410     } // if ( beaglemode )
0411 
0412     if ( legacymilou && inEvent->BeamLepton()->GetChild1Index()==0 ){
0413       if ( !milouwarn ){
0414     cout << "Warning: Trying to repair legay Milou's parentage issues." << endl;
0415     cout << endl;
0416     milouwarn=true; 
0417       }
0418 
0419       // e
0420       inEvent->GetTrack(1-1)->SetChild1Index(3);
0421       inEvent->GetTrack(1-1)->SetChildNIndex(4);
0422 
0423       // p
0424       inEvent->GetTrack(2-1)->SetChild1Index(6);
0425       inEvent->GetTrack(2-1)->SetChildNIndex(0);
0426 
0427       // e'
0428       inEvent->GetTrack(3-1)->SetParentIndex(1);
0429       inEvent->GetTrack(3-1)->SetChild1Index(0);
0430       inEvent->GetTrack(3-1)->SetChildNIndex(0);
0431 
0432       // gamma*
0433       inEvent->GetTrack(4-1)->SetParentIndex(1);
0434       inEvent->GetTrack(4-1)->SetChild1Index(5);
0435       inEvent->GetTrack(4-1)->SetChildNIndex(0);
0436       
0437       // gamma
0438       inEvent->GetTrack(5-1)->SetParentIndex(4);
0439       inEvent->GetTrack(5-1)->SetChild1Index(0);
0440       inEvent->GetTrack(5-1)->SetChildNIndex(0);
0441       
0442       // p'
0443       inEvent->GetTrack(6-1)->SetParentIndex(2);
0444       inEvent->GetTrack(6-1)->SetChild1Index(0);
0445       inEvent->GetTrack(6-1)->SetChildNIndex(0);
0446 
0447       // ISR 
0448       if (inEvent->GetTrack(7-1) ){ 
0449     inEvent->GetTrack(7-1)->SetParentIndex(0);
0450     inEvent->GetTrack(7-1)->SetChild1Index(0);
0451     inEvent->GetTrack(7-1)->SetChildNIndex(0);
0452       }
0453     }
0454     
0455     // First, fix sloppily implemented mother-daughter relations
0456     // Not done for BeAGLE, because of the special vertex
0457     if ( !beaglemode ){
0458       for( unsigned int t=0; t<inEvent->GetNTracks(); ++t) {
0459     const Particle* inParticle = inEvent->GetTrack(t);
0460     
0461     // Do my children know me?
0462     auto myindex = inParticle->GetIndex();
0463     // std::cout << "Processing track " << t << " with index " << myindex << std::endl;
0464     auto c1 = inParticle->GetChild1Index();
0465     auto cN = inParticle->GetChildNIndex();
0466     if ( cN==0 ) cN =c1;
0467     if ( c1>cN ) std::swap(c1,cN);
0468     if ( c1>0 ) {
0469       
0470       //In a small number of Djangoh events, the particle list will be incomplete.
0471       //A particle will have a child which is not included in the particle list.
0472       bool djangohproblem = false;
0473 
0474       for ( UShort_t c = c1; c<=cN; ++c ){ // sigh. index starts at 1, tracks at 0;
0475         Particle* child = inEvent->GetTrack(c-1);
0476         if ( !child ) {
0477           cerr << "Trying to access a non-existant child" << endl;
0478           cerr << "Event is " << i << "  Problem index is " << c << endl;
0479           cerr << "If this is not a djangoh file, please contact the eic-smear developers"<<endl;
0480           djangohproblem = true;
0481           break;
0482         }
0483  
0484         // std::cout << "     Processing child with index " << child->GetIndex() << std::endl;
0485         auto p1 = child->GetParentIndex();
0486         auto pN = child->GetParentIndex1();
0487         if ( p1>pN ) std::swap(p1,pN);
0488         if ( p1==0 && pN==0 ){ // child erroneously believes to be motherless
0489           child->SetParentIndex( myindex );
0490         } else if ( p1==0 ) { // We are the only parent, is it correctly assigned?
0491           if ( pN != myindex ){
0492         // Nothing we can do, e.g. pythia allows multiple parenthood but lacks a way to describe that, see:
0493         // 12     12       2101        5       18       31
0494         // ...
0495         // 16     11          2       10       18       31
0496         // ...
0497         // 26      1       -211       12        0        0
0498         // 27      1        211       16        0        0
0499         // cerr << "My child thinks its mother is " << pN << ", but it should be " << myindex << endl;
0500         // return -1;
0501           }
0502         } else {
0503           // If multiple parents come from non-BeAGLE MC's revisit
0504           cout << "Found more than one parent in a non-BeAGLE file. Please contact the authors." << endl;
0505           return -1;
0506           // We have more than one parent, are they correct?
0507           // This would be the logic if p1 and pN _span_
0508           // if ( myindex < p1 || myindex > pN ){
0509           //   std::cout << "Processing event " << i << std::endl;
0510           //   std::cout << "Processing track " << t << " with index " << myindex << std::endl;
0511           //   std::cout << "     Processing child with index " << child->GetIndex() << std::endl;
0512           //   cerr << "My child thinks its mothers range between " << p1 << " and " << pN
0513           //       << ", but I am " << myindex << endl;
0514           //   // return -1;
0515           // }
0516           // Instead, it seems that BeAGLE (mostly?) assumes this to mean
0517           // exactly two parents, usually far apart in index
0518           // if ( myindex != p1 && myindex != pN ){
0519           //   // Problematic situation in BeAGLE:
0520           //   //  I       S        PID       P1       P2       D1       D2
0521           //   // ==========================================================
0522           //   //  17     18       2112        0        0      260      261
0523           //   // ...
0524           //   // 254     19        111       41      244      260      261
0525           //   // 255      2       2212       41      244      260      261
0526           //   // ...
0527           //   // 260     16       2112       17      254        0        0
0528           //   // 261     16       2212       17      254        0        0
0529           // }
0530         }
0531       }
0532       if(djangohproblem) continue;
0533     }
0534     // Do my parents acknowledge me?
0535     auto p1 = inParticle->GetParentIndex();
0536     auto pN = inParticle->GetParentIndex1();
0537     if ( p1>pN ) std::swap(p1,pN);
0538     if ( p1==0 ) p1 =pN;
0539     // Do all my parents acknowledge me?
0540     if (pN > 0){
0541       for ( unsigned int p = p1; p<=pN ; ++p ){
0542       Particle* parent = inEvent->GetTrack(p-1);
0543       auto pc1 = parent->GetChild1Index();
0544       auto pcN = parent->GetChildNIndex();
0545       if ( pc1>pcN ) std::swap(pc1,pcN);
0546       if ( pc1 > myindex ){
0547         // cout << "hello1" << endl;
0548         parent->SetChild1Index( myindex );
0549       }
0550       if ( pcN < myindex ){
0551         // cout << "hello2" << endl;
0552         parent->SetChildNIndex( myindex );
0553       }
0554       }
0555     }      
0556       }
0557     } // graph repair for !beaglemode
0558       
0559     // Perform consistency checks and collect particles
0560     std::vector<GenParticlePtr> hepevt_particles;
0561     hepevt_particles.reserve( inEvent->GetNTracks() );
0562     for( unsigned int t=0; t<inEvent->GetNTracks(); ++t) {
0563       const Particle* inParticle = inEvent->GetTrack(t);
0564       // Particles with status 1 cannot have children
0565       auto status = inParticle->GetStatus();
0566       if ( status==1 ){
0567     if (inParticle->GetNChildren() != 0 ){
0568       cout << "Status is 1 but we have children?" << endl;
0569       inParticle->Print();
0570     } 
0571       }
0572       
0573       // // All child-less particles should have a "safe" status (like 21), best would be 1
0574       // if (inParticle->GetNChildren() == 0 ){
0575       //    if ( status !=1 && status !=21 ){
0576       //      cerr << "Status is " << status << " but we have no children?" << endl;
0577       //      inParticle->Print();
0578       //    }
0579       // }
0580 
0581       // // Mother-less particles should be the beam only
0582       // Alas, that's not the case :-/
0583       // if ( t>1 && inParticle->GetParentIndex()==0 && inParticle->GetParentIndex1() ==0 ){
0584       //    cout << "Event: " << i << " -- We have no mother" << endl;
0585       //    inParticle->Print();
0586       // }
0587 
0588       FourVector pv = FourVector( inParticle->GetPx(), inParticle->GetPy(), inParticle->GetPz(),inParticle->GetE() );
0589       int statusHepMC = inParticle->GetStatus();
0590       // We should use only 1 (final), 2 (decayed hadron or lepton), 4 (beam), and >10, <=200 (anything else)
0591       // This may need to be decided on a generator-by-generator basis
0592       // We can assume final particles already have status 1, because that's
0593       // what EventMC::FinalState uses (and it's not overridden in existing classes)
0594 
0595       // Catch decayed leptons and hadrons
0596       // doesn't work for BeAGLE
0597       if ( ! beaglemode && t>3 ){     // Ignore the beam
0598     if (inParticle->GetNChildren() != 0 ){ // ignore final particles
0599       auto pdg = TDatabasePDG::Instance()->GetParticle( inParticle->Id() );
0600       if ( pdg ){ // ignore unknown particles (e.g. pomerons, ions)
0601         if ( TString(pdg->ParticleClass()).Contains("Lepton")
0602          || TString(pdg->ParticleClass()).Contains("Baryon")
0603          || TString(pdg->ParticleClass()).Contains("Meson")
0604          ){
0605           // Now our status should be 2!
0606           // cout << statusHepMC << endl;
0607           // inParticle->Print();
0608           statusHepMC = 2;
0609         }
0610       }
0611     }
0612       }
0613 
0614       // // catch DJANGOH trying to assign 4 to non beams
0615       // if ( t>3  && (statusHepMC == 4 ) ){
0616       //      cout << "Naughty Djangoh" << endl;
0617       //      statusHepMC=14;
0618       // }
0619       
0620       // force everything else to be legal
0621       if ( statusHepMC != 1 && statusHepMC != 2 && statusHepMC != 4 ){
0622     while ( statusHepMC <=10 ) statusHepMC+=10;
0623     while ( statusHepMC >200 ) statusHepMC-=10;
0624       }
0625 
0626       //In the Pythia 6 convention, status=4 indicates a particle which could
0627       //have decayed but did not within the allowed volume around the vertex.
0628       //We adjust these particles to have status=1 in the HepMC file to avoid
0629       //confusion with the beam particles.
0630       if( statusHepMC == 4) statusHepMC = 1;
0631 
0632       // Create GenParticle
0633       hepevt_particles.push_back( std::make_shared<GenParticle>( pv, inParticle->Id(), statusHepMC ));
0634       hepevt_particles.back()->set_generated_mass( inParticle->GetM() );
0635 
0636     }
0637 
0638     // Build the event
0639     // beam particles
0640     // --------------
0641     // Default is 1 = e-, 2 = hadron, 3 = scattered e-, 4 = exchange boson
0642     // But this can (and does) differ, especially for the scattered lepton
0643     // As always, be aware of Fortran starting to count at 1
0644     // Vertex: We don't keep track of time
0645     auto lepton=inEvent->BeamLepton();
0646     int index_lepton = lepton->GetIndex();
0647     if ( index_lepton !=1 ) std::cout << "Warning: Found BeamLepton at " << index_lepton << endl;
0648     auto hep_lepton = hepevt_particles.at( index_lepton-1);
0649     hep_lepton->set_status(4);
0650 
0651     auto boson=inEvent->ExchangeBoson();
0652     int index_boson = boson->GetIndex();
0653     // This happens in Sartre who puts the boson at 3
0654     // if ( index_boson !=4 ) std::cout << "Warning: Found ExchangeBoson at " << index_boson << endl;
0655     // if ( boson->GetParentIndex() != index_lepton && boson->GetParentIndex1() != index_lepton ){
0656     //   // This is common for Sartre, and any others that treat the boson like the beam
0657     //   std::cout << "Warning: ExchangeBoson doesn't recognize the beam as its mother " << endl;
0658     // }
0659     auto hep_boson = hepevt_particles.at( index_boson-1);
0660     // if needed / desired, could force hep_boson->set_status(4);
0661 
0662     GenVertexPtr v_lepton = std::make_shared<GenVertex>();
0663     v_lepton->add_particle_in  (hep_lepton);
0664     v_lepton->add_particle_out (hep_boson);
0665     hepmc3evt.add_vertex(v_lepton);
0666 
0667     auto hadron=inEvent->BeamHadron();
0668     int index_hadron = hadron->GetIndex();
0669     if ( index_hadron !=2 ) std::cout << "Warning: Found BeamHadron at " << index_hadron << endl;
0670     auto hep_hadron = hepevt_particles.at( index_hadron-1);
0671     hep_hadron->set_status(4);
0672     GenVertexPtr v_hadron = std::make_shared<GenVertex>();
0673 
0674     v_hadron->add_particle_in (hep_hadron);
0675     hepmc3evt.add_vertex(v_hadron);
0676 
0677     // For Beagle, use
0678     //                  
0679     //  e      e'               
0680     //   \v1__/                 i1      f1
0681     //         \_gamma        /    \   /
0682     //                 \ _v2_/__i2__ v3__f2
0683     //                 /     \      / \                (*)
0684     //             proton     \iN /    \fN
0685     //
0686     // where i1, .., iN are intermediate (!=1) and f1,..fN are final
0687     // v2 == v_hadron, v3 == v_beagle_final
0688 
0689     // Addendum: BeAGLE does support hadron/lepton decay. Attach the root to v3,
0690     // and keep their children, e.g.:
0691     // 
0692     //   v2_/__i2__ v3__J/psi__e
0693     //                \      \                          (*)
0694     //                 \fN    e
0695     //
0696     // (But also keep decay chains)
0697     // (*) comment lines ending in "\" generate compiler warnings 
0698     
0699     // Dummy to act as a catchall for intermediary particles in beagle
0700     GenVertexPtr v_beagle_final = std::make_shared<GenVertex>();
0701     if ( beaglemode ){
0702       v_hadron->add_particle_in(hep_boson);
0703       hepmc3evt.add_vertex( v_beagle_final );
0704       // We don't have a connection yet, so in the pathological case
0705       // that there are no non-final particles at all, this vertex floats free.
0706       // That's too unlikely to occur to build in a fail-safe now
0707     }
0708     
0709     // Now work our way through the remaining particles
0710     // - Attach each particle that has a mother to their end vertex
0711     // ---> Create / overwrite production vertex in the process.
0712     //      If it's inconsistent, there's not much we can do
0713     // - attach motherless particles to the exchange boson
0714     // ---> In that case, leave the production vertex location in peace
0715     // ---> Also note that ISR photons are motherless and thus
0716     //      get attached to the exchange boson as well
0717     // Topological order should just translate to the fact that
0718     // children always have a higher index than their parents
0719 
0720     // Note: Multiple parents would wreak havoc here - have to handle BeAGLE differently
0721     for( unsigned int t=0; t<inEvent->GetNTracks(); ++t) {
0722       const Particle* inParticle = inEvent->GetTrack(t);
0723 
0724       // Skip what we already have
0725       int index = inParticle->GetIndex();
0726       if ( index==index_lepton || index==index_boson || index==index_hadron) continue;
0727       auto hep_in = hepevt_particles.at( index-1);
0728       // auto hep_mom = hep_boson;
0729       auto hep_mom = hep_hadron;
0730       int momindex = inParticle->GetParentIndex();
0731       auto statusHepMC = inParticle->GetStatus();
0732 
0733       //For Djangoh events which fail to hadronize, shift the parent of the
0734       //final-state parton from the incoming electron beam to th hadron beam     
0735       if (branchClass->InheritsFrom("erhic::EventDjangoh")){
0736         if( statusHepMC==1 && momindex==1 &&
0737       (abs(inParticle->Id())==1 || abs(inParticle->Id())==2 || abs(inParticle->Id())==3 || 
0738        abs(inParticle->Id())==90 || inParticle->Id()==91 || inParticle->Id()==92) ){ 
0739         momindex+=1;
0740     }
0741       }
0742       
0743       // suppress all the intermediate nucleons
0744       // this may be worth doing anyway  just to reduce filesize
0745       if ( beaglemode && statusHepMC==3 ) continue;
0746       if ( beaglemode && statusHepMC==14 ) continue;
0747       if ( beaglemode && statusHepMC==18 ) continue;
0748       if ( beaglemode && statusHepMC==12 ) continue;
0749       // This is purely for legibility, these particles should stay!
0750       // note: 80000 are lighter ions, without specification
0751       // if ( beaglemode && statusHepMC==1 && momindex == beagle_final_index
0752       //       && ( hep_in->pid() == 2112 || hep_in->pid() == 2212 || hep_in->pid() == 80000 ) ) continue;
0753       
0754       // beagle finals 
0755       if ( momindex == beagle_final_index ){
0756     v_beagle_final->add_particle_out(hep_in);
0757     continue;
0758       }
0759 
0760       // beagle intermediates
0761       // out will be handled, but need to attach as incoming
0762       if ( beaglemode && statusHepMC!=1 && statusHepMC!=2  ){
0763     v_beagle_final->add_particle_in(hep_in);
0764       }
0765       
0766       // Mother?
0767       if ( momindex > 0 ){
0768     hep_mom = hepevt_particles.at( momindex-1);
0769       }
0770       
0771       // Does mom have an end vertex yet?
0772       auto momend = hep_mom->end_vertex();
0773       if (!momend) {
0774     momend = std::make_shared<GenVertex>();
0775     momend->add_particle_in(hep_mom);
0776     hepmc3evt.add_vertex(momend);
0777       }
0778 
0779       momend->add_particle_out(hep_in);
0780     
0781       // update prod vertex?
0782       if ( momindex > 1){
0783     auto vnew = inParticle->GetVertex();
0784     momend->set_position( FourVector( vnew.x(), vnew.y(), vnew.z(), 0));
0785       }
0786       // file->write_event(hepmc3evt);
0787     }
0788 
0789     // Done! Write the event.
0790     file->write_event(hepmc3evt);
0791     // There's a bunch of cleanup one should do now, with all the dynamical
0792     // vertices and particles. BUT shared_ptr should take care of that. Revisit if there are memory leaks.
0793     // break;
0794     
0795   }  // event loop
0796 
0797   file->close();
0798   
0799   Long64_t result = 0;
0800   
0801   return result;
0802 }
0803 
0804 
0805 /**
0806 Deprecated legacy wrapper */
0807 
0808 Long64_t TreeToHepMC(const std::string& inputFileName,
0809              const std::string& outputDirName,
0810              Long64_t maxEvent,
0811              const bool createHepMC2){
0812   if ( createHepMC2 ) {
0813     cout << "Warning, this interface is deprecated, please use:" << endl;
0814     cout << "TreeToHepMC(\""<< inputFileName<< "\",\""<< outputDirName
0815      << "\","<< maxEvent << ","
0816      << "erhic::HepMC_outtype::HepMC2)"
0817      << endl;
0818     return TreeToHepMC(inputFileName,outputDirName,maxEvent,erhic::HepMC_outtype::HepMC2);
0819   } else {
0820     cout << "Warning, this interface is deprecated, please use:" << endl;
0821     cout << "TreeToHepMC(\""<< inputFileName<< "\",\""<< outputDirName
0822      << "\","<< maxEvent << ","
0823      << "erhic::HepMC_outtype::HepMC3)"
0824      << endl;
0825     return TreeToHepMC(inputFileName,outputDirName,maxEvent,erhic::HepMC_outtype::HepMC3);
0826   }
0827 
0828       
0829   //   outtype == erhic::HepMC_outtype::HepMC3 ) {
0830   //   return TreeToHepMC(inputFileName, outputDirName,maxEvent,false);
0831   // }
0832   // if ( outtype == erhic::HepMC_outtype::HepMC2 ) {
0833   //   return TreeToHepMC(inputFileName, outputDirName,maxEvent,true);
0834   // }
0835   return -1;
0836 }
0837 
0838