Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2024-09-28 07:02:50

0001 /**
0002    \file
0003    Implementation of class erhic::EventFactoryHepMC.
0004 
0005    \author    Chris Pinkenburg, Kolja Kauder
0006    \date      2020-07-07
0007    \copyright 2020 Brookhaven National Lab
0008 */
0009 
0010 #include "eicsmear/erhic/EventFactoryHepMC.h"
0011 
0012 #include <memory>
0013 #include <stdexcept>
0014 #include <string>
0015 
0016 #include <TClass.h>
0017 #include <TProcessID.h>
0018 
0019 #include "eicsmear/erhic/BeamParticles.h"
0020 #include "eicsmear/erhic/EventPythia.h"
0021 #include "eicsmear/erhic/EventHepMC.h"
0022 #include "eicsmear/erhic/EventMilou.h"
0023 #include "eicsmear/erhic/EventDjangoh.h"
0024 #include "eicsmear/erhic/EventDpmjet.h"
0025 #include "eicsmear/erhic/EventRapgap.h"
0026 #include "eicsmear/erhic/EventPepsi.h"
0027 #include "eicsmear/erhic/EventGmcTrans.h"
0028 #include "eicsmear/erhic/EventSimple.h"
0029 #include "eicsmear/erhic/EventDEMP.h"
0030 #include "eicsmear/erhic/EventSartre.h"
0031 #include "eicsmear/functions.h"  // For getFirstNonBlank()
0032 #include "eicsmear/erhic/Kinematics.h"
0033 #include "eicsmear/erhic/ParticleIdentifier.h"
0034 #include "eicsmear/erhic/ParticleMC.h"
0035 
0036 #include <HepMC3/ReaderAsciiHepMC2.h>
0037 #include <HepMC3/ReaderAscii.h>
0038 #include "HepMC3/GenVertex.h"
0039 // The newer ReaderFactory is header-only and can be used for older versions
0040 // This file is copied verbatim from https://gitlab.cern.ch/hepmc/HepMC3
0041 // Copyright (C) 2014-2020 The HepMC collaboration, licensed under GPL3
0042 #include <HepMC3/Version.h>
0043 #if HEPMC3_VERSION_CODE < 3002004
0044 #include <eicsmear/HepMC_3_2_4_ReaderFactory.h>
0045 #else
0046 #include <HepMC3/ReaderFactory.h>
0047 #endif
0048 
0049 #include <TVector3.h>
0050 #include <TParticlePDG.h>
0051 #include <TLorentzVector.h>
0052 #include <TDatabasePDG.h>
0053 #include <TObjArray.h>
0054 #include <TObjString.h>
0055 
0056 #include <map>
0057 #include <vector>
0058 #include <algorithm> // for *min_element, *max_element
0059 
0060 using std::cout;
0061 using std::cerr;
0062 using std::endl;
0063 using std::map;
0064 using std::vector;
0065 
0066 namespace erhic {
0067 
0068   // Use this struct to automatically reset TProcessID object count.
0069   struct TProcessIdObjectCount {
0070     // Initialse object with current TProcessID object count.
0071     TProcessIdObjectCount() {
0072       count = TProcessID::GetObjectCount();
0073     }
0074     // Restore object count to the value at initialisation.
0075     // See example in $ROOTSYS/test/Event.cxx
0076     // To save space in the table keeping track of all referenced objects
0077     // we assume that our events do not address each other.
0078     ~TProcessIdObjectCount() {
0079       TProcessID::SetObjectCount(count);
0080     }
0081     int count;
0082   };
0083 
0084   template<>
0085   bool EventFromAsciiFactory<erhic::EventHepMC>::AtEndOfEvent() const {return false;}
0086 
0087   template<>
0088   void EventFromAsciiFactory<erhic::EventHepMC>::FindFirstEvent()  {
0089     // nothing to do
0090   }
0091 
0092   template<>
0093   bool EventFromAsciiFactory<erhic::EventHepMC>::AddParticle() {
0094     try {
0095       // open only once
0096       // otherwise this gets called for every event
0097       // and we can't make it a member since we're based on the EventFromAsciiFactory template
0098       static std::shared_ptr<HepMC3::Reader> adapter = HepMC3::deduce_reader(*mInput); 
0099 
0100       if (mEvent.get()) {
0101         HepMC3::GenEvent evt(HepMC3::Units::GEV,HepMC3::Units::MM);
0102     adapter->read_event(evt);
0103     if ( adapter->failed() ) return false;
0104 
0105     // We could take all particles "in order"
0106     // This isn't well defined - depends on traversal of either particles()
0107     // or for (auto& v : evt.vertices() ){ for (auto& p : v->particles_out() ) {}}
0108     // Then we would also have to overwrite
0109     // EventMC::BeamLepton(), EventMC::BeamHadron(), EventMC::ExchangeBoson(), EventMC::ScatteredLepton().
0110     // and worry about the logic when the file is used.
0111 
0112     // Alternatively, we can enforce standard order
0113     // BeamLepton, BeamHadron, ScatteredLepton, ExchangeBoson
0114     // That way we only need to do this logic once.
0115     // Need to keep track of daughter-mother relationship for that.
0116 
0117     int particleindex;
0118     particleindex = 1;
0119     // Can't use GenParticle::children() because they don't have indices assigned yet
0120     // map each HepMC particle onto its corresponding particleindex
0121     std::map < HepMC3::GenParticlePtr, int > hepmcp_index;
0122     hepmcp_index.clear();
0123     
0124     // start with the beam
0125     // Boring determination of the order:
0126     auto beams = evt.beams();
0127     // for ( auto& p : beams ){
0128     //   cout << p << endl;
0129     // }
0130     // cout << beams.size() << endl;
0131     // throw(-1);
0132     assert ( beams.size() == 2 );
0133     auto hadron = beams.at(0); // or nucleon
0134     auto lepton = beams.at(1);
0135 
0136     // Before going on to handle normal events, catch special cases
0137     
0138     // Special beam-gas-only events
0139     if ( hadron->pid()==22 || lepton->pid()==22 ){
0140       if ( evt.particles().size() > 2 ){
0141         cout << "Warning: Treating the event as beam gas but found "
0142          << evt.particles().size() << " particles" << endl;
0143       }
0144       auto photon=hadron; // for clarity use the right name
0145       if ( lepton->pid()==22 ) {
0146         std::swap (lepton, photon);
0147       }
0148 
0149       
0150       // Still need beam particles (otherwise fun4all is confused)
0151       auto beam_e_mom = lepton->momentum() + photon->momentum();
0152       // cout << beam_e_mom.x() << " "  
0153       //      << beam_e_mom.y() << " "
0154       //      << beam_e_mom.z() << " "
0155       //      << beam_e_mom.e() << endl;
0156       auto beam_e = std::make_shared<HepMC3::GenParticle>( beam_e_mom, 11, 21 );
0157       auto v = lepton->production_vertex();
0158       v->add_particle_in  (beam_e);
0159 
0160       // make up a proton
0161       auto pz_p = -10.0 *beam_e_mom.z();
0162       auto e_p = sqrt ( pow(pz_p,2) + pow(0.938,2) );
0163       HepMC3::FourVector beam_p_mom ( 0,0,pz_p, e_p);
0164       auto beam_p = std::make_shared<HepMC3::GenParticle>( beam_p_mom, 2212, 21 );
0165 
0166       HandleHepmcParticle( beam_e, hepmcp_index, particleindex, mEvent );
0167       HandleHepmcParticle( beam_p, hepmcp_index, particleindex, mEvent );
0168       
0169       HandleHepmcParticle( lepton, hepmcp_index, particleindex, mEvent );
0170       HandleHepmcParticle( photon, hepmcp_index, particleindex, mEvent );
0171 
0172       // x-setion etc.
0173       UpdateRuninfo( mObjectsToWriteAtTheEnd, evt );
0174       // We're done. Avoid FinishEvent()
0175       return true;
0176     }
0177          
0178     // Find the lepton
0179     auto pdgl = TDatabasePDG::Instance()->GetParticle( lepton->pid() );
0180     auto pdgh = TDatabasePDG::Instance()->GetParticle( hadron->pid() );
0181     // Sigh. TParticlePDG uses C strings for particle type. I refuse to use strcmp
0182     // Could test individual pid's instead.
0183     bool b0islepton = (string(pdgl->ParticleClass()) == "Lepton");
0184     bool b1islepton = (string(pdgh->ParticleClass()) == "Lepton");
0185 
0186     // Catch h+h (and, untested, l+l) collisions. Avoid DIS stuff altogether
0187     if ( b0islepton == b1islepton ){
0188       // // exactly one of them should be a lepton.
0189       // throw std::runtime_error ("Exactly one beam should be a lepton - please contact the authors for ff or hh beams");
0190 
0191       // Just go over all vertices and handle the rest.
0192       HandleAllVertices(evt, hepmcp_index, particleindex, mEvent);
0193 
0194       // x-setion etc.
0195       UpdateRuninfo( mObjectsToWriteAtTheEnd, evt );
0196       // We're done. Avoid FinishEvent()
0197       return true;
0198     }
0199     
0200     if ( !b0islepton ) {
0201       std::swap (lepton, hadron);
0202     }
0203     // careful, don't try to use pdg[lh], b[01]islepton from here on out;
0204     // they don't get swapped along
0205 
0206     // now find the scattered lepton and potentially the gamma
0207     // some processes (ff2ff) don't have a gamma!
0208     HepMC3::GenParticlePtr scatteredlepton=0;
0209     HepMC3::GenParticlePtr photon=0;
0210 
0211     // we can handle one or two only
0212     if ( lepton->children().size() >2 || lepton->children().size()==0 ){
0213       cerr << "electron has " << lepton->children().size() << " daughters." << endl;
0214       throw std::runtime_error ("Wrong number of lepton daughters; should be 1 (lepton) or 2 (lepton+boson).");
0215     }
0216 
0217     // if one exists, it's a daughter of the beam lepton, and its sibling is the lepton (which isn't necessarily final yet though)
0218     if ( lepton->children().size() == 2 ){
0219       scatteredlepton = lepton->children().at(0);
0220       photon = lepton->children().at(1);
0221       if ( scatteredlepton->pid() != 22 && photon->pid() != 22 ){
0222         cerr << "lepton child 1 pid = " << scatteredlepton->pid() << endl;
0223         cerr << "lepton child 2 pid = " << photon->pid() << endl;
0224         throw std::runtime_error ("Found two beam lepton daughters, none or both of them a photon.");
0225       }
0226       if ( photon->pid() != 22 ){
0227         std::swap ( photon, scatteredlepton );
0228       }
0229     }
0230 
0231     // no exchange boson
0232     if ( lepton->children().size() == 1 ){
0233       scatteredlepton = lepton->children().at(0);
0234       auto pdgs = TDatabasePDG::Instance()->GetParticle( scatteredlepton->pid() );
0235       if ( !TString(pdgs->ParticleClass()).Contains("Lepton") ){
0236         throw std::runtime_error ("Found one beam lepton daughter, and it is not a lepton.");
0237       }
0238 
0239       // We could play games and make one up:
0240       // auto photonmom = lepton->momentum() - scatteredlepton->momentum();
0241       // photon = std::make_shared<HepMC3::GenParticle>( photonmom, 22, 13 );
0242       // photon->set_momentum(photonmom);
0243       // photon->set_pid( 22 );
0244       // // And add to the vertex (meaning the photon now has the lepton as a mother)
0245       // scatteredlepton->production_vertex()->add_particle_out(photon);
0246       // But the cleaner solution seems to be to save a 0 particle in the photon spot
0247       photon=std::make_shared<HepMC3::GenParticle>();
0248        // we'll catch this in the ParticleIdentifier
0249       photon->set_pid( 0 );
0250       photon->set_status( 0 );
0251       // Need to give a production vertex to avoid segfaulting
0252       scatteredlepton->production_vertex()->add_particle_out(photon);
0253     }
0254 
0255     // Follow the lepton
0256     // Assumptions (otherwise this code will break...)
0257     // - it can onlhy branch into l -> l + X, where X is not a lepton
0258     // -> we can traverse children and immediately follow any lepton,
0259     //    we won't miss a "second" lepton
0260     // -> we can go until we run out of children, the lepton won't decay
0261     //    or we can go until the status is final, the result should be the same
0262     // There's no clear-cut final() method though, so we go through the end and hope!
0263     // avoid endless loop
0264     bool foundbranch=true;
0265     while ( scatteredlepton->children().size() > 0 ){
0266       if ( !foundbranch ){
0267         throw std::runtime_error ("none of this lepton's children is a lepton.");
0268       }
0269       foundbranch=false;
0270       for ( auto& c : scatteredlepton->children() ){
0271         auto pdgl = TDatabasePDG::Instance()->GetParticle( c->pid() );
0272         if ( string(pdgl->ParticleClass()) == "Lepton" ){
0273           // found the correct branch,
0274           // update and break out of for loop,
0275           // resume while loop with new candidate
0276           scatteredlepton = c;
0277           foundbranch=true;
0278           break;
0279         }
0280       }
0281     }
0282 
0283     // Now add all four in the right order
0284     HandleHepmcParticle( lepton, hepmcp_index, particleindex, mEvent );
0285     HandleHepmcParticle( hadron, hepmcp_index, particleindex, mEvent );
0286     HandleHepmcParticle( scatteredlepton, hepmcp_index, particleindex, mEvent );
0287     HandleHepmcParticle( photon, hepmcp_index, particleindex, mEvent );
0288 
0289     // Now go over all vertices and handle the rest.
0290     // Note that by default this could double-count what we just did
0291     // But the method takes care of this with a lookup table
0292     HandleAllVertices(evt, hepmcp_index, particleindex, mEvent);
0293 
0294     // x-setion etc.
0295     UpdateRuninfo( mObjectsToWriteAtTheEnd, evt );
0296       }  // if ( mEvent.get() )
0297 
0298       auto finished = FinishEvent(); // 0 is success
0299       return (finished==0);
0300     }  // try
0301     catch(std::exception& error) {
0302       std::cerr << "Exception building particle: " << error.what() << std::endl;
0303       return false;
0304     }
0305   }
0306 
0307   template<>
0308   erhic::EventHepMC* EventFromAsciiFactory<erhic::EventHepMC>::Create()
0309   {
0310     TProcessIdObjectCount objectCount;
0311     mEvent.reset(new erhic::EventHepMC());
0312     if (!AddParticle()) {
0313       mEvent.reset(nullptr);
0314     }  // if
0315 
0316     return mEvent.release();
0317   }
0318   // -----------------------------------------------------------------------
0319   
0320   void HandleHepmcParticle( const HepMC3::GenParticlePtr& p, std::map < HepMC3::GenParticlePtr, int >& hepmcp_index, int& particleindex, std::unique_ptr<erhic::EventHepMC>& mEvent ){
0321     // do nothing if we already used this particle
0322     auto it = hepmcp_index.find(p);
0323     if ( it != hepmcp_index.end() ) return;
0324 
0325     // Create the particle
0326     ParticleMC particle;
0327     auto v = p->production_vertex();
0328     TVector3 vertex;
0329     if ( v ) vertex = TVector3(v->position().x(),v->position().y(),v->position().z());
0330 
0331     particle.SetVertex(vertex);
0332     // takes care of  xv, yv, zv;
0333 
0334     TLorentzVector lovec(p->momentum().x(),p->momentum().y(),p->momentum().z(),p->momentum().e());
0335     particle.Set4Vector(lovec);
0336     // takes care of  E, px, py, pz, m, and
0337     // derived quantities: pt, p, rapidity, eta, theta, phi
0338 
0339     // fill up the missing parts
0340     particle.SetId( p->pid() );
0341     particle.SetStatus(p->status());
0342 
0343     // Index: Runs from 1 to N.
0344     particle.SetIndex ( particleindex );
0345 
0346     // remember this HepMC3::GenParticlePtr <-> index connection
0347     hepmcp_index[p] = particleindex;
0348 
0349     particleindex++;
0350 
0351     particle.SetEvent(mEvent.get());
0352     mEvent->AddLast(&particle);
0353   }
0354 
0355   // -----------------------------------------------------------------------
0356   void HandleAllVertices( HepMC3::GenEvent& evt, std::map < HepMC3::GenParticlePtr, int >& hepmcp_index, int& particleindex, std::unique_ptr<erhic::EventHepMC>& mEvent ){
0357     // Note that by default this could double-count previous addiions
0358     // Instead of trying to find out which vertex to skip, just use the lookup table
0359     // (inside HandleHepmcParticle) to avoid this.
0360     for (auto& v : evt.vertices() ){
0361       for (auto& p : v->particles_out() ) {
0362     HandleHepmcParticle( p, hepmcp_index, particleindex, mEvent );
0363       }
0364     }
0365     
0366     // Now the map has built up full 1-1 correspondence between all hepmc particles
0367     // and the ParticleMC index.
0368     // So we can loop over the particles again, find their parents and offspring, and map accordingly.
0369     // We explicitly take advantage of particle index = Event entry # +1
0370     // If that changes, we'll need to maintain a second map
0371     // Note: the beam proton appears twice; that's consistent with the behavior of pythia6
0372     for (auto& v : evt.vertices() ){
0373       for (auto& p : v->particles_out() ) {
0374     // corresponding ParticleMC is at
0375     int treeindex = hepmcp_index[p]-1;
0376     assert ( treeindex >=0 ); // Not sure if that can happen. If it does, we could probably just continue;
0377     auto track = mEvent->GetTrack(treeindex);
0378     
0379     // collect all parents
0380     vector<int> allparents;
0381     for (auto& parent : p->parents() ) {
0382       allparents.push_back( hepmcp_index[parent] );
0383     }
0384     // orig and orig1 aren't very intuitively named...
0385     // For pythia6, orig is the default, and orig1 seems purely a placeholder
0386     // trying to mimick that here.
0387     if ( allparents.size() == 1){
0388       track->SetParentIndex( allparents.at(0) );
0389     }
0390     if ( allparents.size() >= 2){
0391       // smallest and highest are stored
0392       track->SetParentIndex( *min_element(allparents.begin(), allparents.end() ) );
0393       track->SetParentIndex1( *max_element(allparents.begin(), allparents.end() ) );
0394     }
0395     
0396     // same for children
0397     vector<int> allchildren;
0398     for (auto& child : p->children() ) {
0399       allchildren.push_back( hepmcp_index[child] );
0400     }
0401     if ( allchildren.size() == 1){
0402       track->SetChild1Index( allchildren.at(0) );
0403     }
0404     if ( allchildren.size() >= 2){
0405       // smallest and highest are stored
0406       track->SetChild1Index( *min_element(allchildren.begin(), allchildren.end() ) );
0407       track->SetChildNIndex( *max_element(allchildren.begin(), allchildren.end() ) );
0408     }
0409       }
0410     }
0411   }
0412   
0413 
0414   // -----------------------------------------------------------------------
0415   void UpdateRuninfo( std::vector<VirtualEventFactory::NamedObjects>& mObjectsToWriteAtTheEnd, 
0416               const HepMC3::GenEvent& evt ){
0417     // Wasteful, since it's only written out for the final event, but
0418     // this class doesn't know when that happens
0419     TString xsec = ""; 
0420     TString xsecerr = "";
0421     if ( auto xsecinfo=evt.cross_section() ){
0422       // not all generators have this info
0423       xsec += xsecinfo->xsec();
0424       xsecerr += xsecinfo->xsec_err();
0425     }
0426     TObjString* Xsec;
0427     TObjString* Xsecerr;
0428     
0429     // // not saved in the examples I used.
0430     // TString trials = ""; trials+= evt.cross_section()->get_attempted_events();
0431     // TString nevents = ""; nevents += evt.cross_section()->get_accepted_events();
0432     // TObjString* Trials;
0433     // TObjString* Nevents;
0434     
0435     if ( mObjectsToWriteAtTheEnd.size() == 0 ){
0436       Xsec = new TObjString;
0437       Xsecerr = new TObjString;
0438       mObjectsToWriteAtTheEnd.push_back ( VirtualEventFactory::NamedObjects( "crossSection", Xsec) );
0439       mObjectsToWriteAtTheEnd.push_back ( VirtualEventFactory::NamedObjects( "crossSectionError", Xsecerr) );
0440       // Trials = new TObjString;
0441       // Nevents = new TObjString;
0442       // mObjectsToWriteAtTheEnd.push_back ( VirtualEventFactory::NamedObjects( "nTrials", Trials) );
0443       // mObjectsToWriteAtTheEnd.push_back ( VirtualEventFactory::NamedObjects( "nEvents", Nevents) );
0444     }
0445     Xsec = (TObjString*) mObjectsToWriteAtTheEnd.at(0).second;
0446     Xsec->String() = xsec;
0447     Xsecerr = (TObjString*) mObjectsToWriteAtTheEnd.at(1).second;
0448     Xsecerr->String() = xsecerr;
0449     // Trials = (TObjString*) mObjectsToWriteAtTheEnd.at(2).second;
0450     // Trials->String() = trials;
0451     // Nevents = (TObjString*) mObjectsToWriteAtTheEnd.at(3).second;
0452     // Nevents->String() = nevents;
0453   }
0454   
0455 }  // namespace erhic
0456 
0457 namespace {
0458 
0459   // Need this to generate the CINT code for each version
0460   erhic::EventFromAsciiFactory<erhic::EventHepMC> eh;
0461 }  // namespace