EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
EventFactoryHepMC.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file EventFactoryHepMC.cxx
1 
11 
12 #include <memory>
13 #include <stdexcept>
14 #include <string>
15 
16 #include <TClass.h>
17 #include <TProcessID.h>
18 
31 #include "eicsmear/functions.h" // For getFirstNonBlank()
35 
36 #include <HepMC3/ReaderAsciiHepMC2.h>
37 #include <HepMC3/ReaderAscii.h>
38 #include "HepMC3/GenVertex.h"
39 // The newer ReaderFactory is header-only and can be used for older versions
40 // This file is copied verbatim from https://gitlab.cern.ch/hepmc/HepMC3
41 // Copyright (C) 2014-2020 The HepMC collaboration, licensed under GPL3
42 #include <HepMC3/Version.h>
43 #if HEPMC3_VERSION_CODE < 3002004
45 #else
46 #include <HepMC3/ReaderFactory.h>
47 #endif
48 
49 #include <TVector3.h>
50 #include <TParticlePDG.h>
51 #include <TLorentzVector.h>
52 #include <TDatabasePDG.h>
53 #include <TObjArray.h>
54 #include <TObjString.h>
55 
56 #include <map>
57 #include <vector>
58 #include <algorithm> // for *min_element, *max_element
59 
60 using std::cout;
61 using std::cerr;
62 using std::endl;
63 using std::map;
64 using std::vector;
65 
66 namespace erhic {
67 
68  // Use this struct to automatically reset TProcessID object count.
69  struct TProcessIdObjectCount {
70  // Initialse object with current TProcessID object count.
72  count = TProcessID::GetObjectCount();
73  }
74  // Restore object count to the value at initialisation.
75  // See example in $ROOTSYS/test/Event.cxx
76  // To save space in the table keeping track of all referenced objects
77  // we assume that our events do not address each other.
79  TProcessID::SetObjectCount(count);
80  }
81  int count;
82  };
83 
84  template<>
86 
87  template<>
89  // nothing to do
90  }
91 
92  template<>
94  try {
95  // open only once
96  // otherwise this gets called for every event
97  // and we can't make it a member since we're based on the EventFromAsciiFactory template
98  static std::shared_ptr<HepMC3::Reader> adapter = HepMC3::deduce_reader(*mInput);
99 
100  if (mEvent.get()) {
101  HepMC3::GenEvent evt(HepMC3::Units::GEV,HepMC3::Units::MM);
102  adapter->read_event(evt);
103  if ( adapter->failed() ) return false;
104 
105  // We could take all particles "in order"
106  // This isn't well defined - depends on traversal of either particles()
107  // or for (auto& v : evt.vertices() ){ for (auto& p : v->particles_out() ) {}}
108  // Then we would also have to overwrite
109  // EventMC::BeamLepton(), EventMC::BeamHadron(), EventMC::ExchangeBoson(), EventMC::ScatteredLepton().
110  // and worry about the logic when the file is used.
111 
112  // Alternatively, we can enforce standard order
113  // BeamLepton, BeamHadron, ScatteredLepton, ExchangeBoson
114  // That way we only need to do this logic once.
115  // Need to keep track of daughter-mother relationship for that.
116 
117  int particleindex;
118  particleindex = 1;
119  // Can't use GenParticle::children() because they don't have indices assigned yet
120  // map each HepMC particle onto its corresponding particleindex
121  std::map < HepMC3::GenParticlePtr, int > hepmcp_index;
122  hepmcp_index.clear();
123 
124  // start with the beam
125  // Boring determination of the order:
126  auto beams = evt.beams();
127  // for ( auto& p : beams ){
128  // cout << p << endl;
129  // }
130  // cout << beams.size() << endl;
131  // throw(-1);
132  assert ( beams.size() == 2 );
133  auto hadron = beams.at(0); // or nucleon
134  auto lepton = beams.at(1);
135 
136  // Before going on to handle normal events, catch special cases
137 
138  // Special beam-gas-only events
139  if ( hadron->pid()==22 || lepton->pid()==22 ){
140  if ( evt.particles().size() > 2 ){
141  cout << "Warning: Treating the event as beam gas but found "
142  << evt.particles().size() << " particles" << endl;
143  }
144  auto photon=hadron; // for clarity use the right name
145  if ( lepton->pid()==22 ) {
146  std::swap (lepton, photon);
147  }
148 
149 
150  // Still need beam particles (otherwise fun4all is confused)
151  auto beam_e_mom = lepton->momentum() + photon->momentum();
152  // cout << beam_e_mom.x() << " "
153  // << beam_e_mom.y() << " "
154  // << beam_e_mom.z() << " "
155  // << beam_e_mom.e() << endl;
156  auto beam_e = std::make_shared<HepMC3::GenParticle>( beam_e_mom, 11, 21 );
157  auto v = lepton->production_vertex();
158  v->add_particle_in (beam_e);
159 
160  // make up a proton
161  auto pz_p = -10.0 *beam_e_mom.z();
162  auto e_p = sqrt ( pow(pz_p,2) + pow(0.938,2) );
163  HepMC3::FourVector beam_p_mom ( 0,0,pz_p, e_p);
164  auto beam_p = std::make_shared<HepMC3::GenParticle>( beam_p_mom, 2212, 21 );
165 
166  HandleHepmcParticle( beam_e, hepmcp_index, particleindex, mEvent );
167  HandleHepmcParticle( beam_p, hepmcp_index, particleindex, mEvent );
168 
169  HandleHepmcParticle( lepton, hepmcp_index, particleindex, mEvent );
170  HandleHepmcParticle( photon, hepmcp_index, particleindex, mEvent );
171 
172  // x-setion etc.
173  UpdateRuninfo( mObjectsToWriteAtTheEnd, evt );
174  // We're done. Avoid FinishEvent()
175  return true;
176  }
177 
178  // Find the lepton
179  auto pdgl = TDatabasePDG::Instance()->GetParticle( lepton->pid() );
180  auto pdgh = TDatabasePDG::Instance()->GetParticle( hadron->pid() );
181  // Sigh. TParticlePDG uses C strings for particle type. I refuse to use strcmp
182  // Could test individual pid's instead.
183  bool b0islepton = (string(pdgl->ParticleClass()) == "Lepton");
184  bool b1islepton = (string(pdgh->ParticleClass()) == "Lepton");
185 
186  // Catch h+h (and, untested, l+l) collisions. Avoid DIS stuff altogether
187  if ( b0islepton == b1islepton ){
188  // // exactly one of them should be a lepton.
189  // throw std::runtime_error ("Exactly one beam should be a lepton - please contact the authors for ff or hh beams");
190 
191  // Just go over all vertices and handle the rest.
192  HandleAllVertices(evt, hepmcp_index, particleindex, mEvent);
193 
194  // x-setion etc.
195  UpdateRuninfo( mObjectsToWriteAtTheEnd, evt );
196  // We're done. Avoid FinishEvent()
197  return true;
198  }
199 
200  if ( !b0islepton ) {
201  std::swap (lepton, hadron);
202  }
203  // careful, don't try to use pdg[lh], b[01]islepton from here on out;
204  // they don't get swapped along
205 
206  // now find the scattered lepton and potentially the gamma
207  // some processes (ff2ff) don't have a gamma!
208  HepMC3::GenParticlePtr scatteredlepton=0;
209  HepMC3::GenParticlePtr photon=0;
210 
211  // we can handle one or two only
212  if ( lepton->children().size() >2 || lepton->children().size()==0 ){
213  cerr << "electron has " << lepton->children().size() << " daughters." << endl;
214  throw std::runtime_error ("Wrong number of lepton daughters; should be 1 (lepton) or 2 (lepton+boson).");
215  }
216 
217  // if one exists, it's a daughter of the beam lepton, and its sibling is the lepton (which isn't necessarily final yet though)
218  if ( lepton->children().size() == 2 ){
219  scatteredlepton = lepton->children().at(0);
220  photon = lepton->children().at(1);
221  if ( scatteredlepton->pid() != 22 && photon->pid() != 22 ){
222  cerr << "lepton child 1 pid = " << scatteredlepton->pid() << endl;
223  cerr << "lepton child 2 pid = " << photon->pid() << endl;
224  throw std::runtime_error ("Found two beam lepton daughters, none or both of them a photon.");
225  }
226  if ( photon->pid() != 22 ){
227  std::swap ( photon, scatteredlepton );
228  }
229  }
230 
231  // no exchange boson
232  if ( lepton->children().size() == 1 ){
233  scatteredlepton = lepton->children().at(0);
234  auto pdgs = TDatabasePDG::Instance()->GetParticle( scatteredlepton->pid() );
235  if ( !TString(pdgs->ParticleClass()).Contains("Lepton") ){
236  throw std::runtime_error ("Found one beam lepton daughter, and it is not a lepton.");
237  }
238 
239  // We could play games and make one up:
240  // auto photonmom = lepton->momentum() - scatteredlepton->momentum();
241  // photon = std::make_shared<HepMC3::GenParticle>( photonmom, 22, 13 );
242  // photon->set_momentum(photonmom);
243  // photon->set_pid( 22 );
244  // // And add to the vertex (meaning the photon now has the lepton as a mother)
245  // scatteredlepton->production_vertex()->add_particle_out(photon);
246  // But the cleaner solution seems to be to save a 0 particle in the photon spot
247  photon=std::make_shared<HepMC3::GenParticle>();
248  // we'll catch this in the ParticleIdentifier
249  photon->set_pid( 0 );
250  photon->set_status( 0 );
251  // Need to give a production vertex to avoid segfaulting
252  scatteredlepton->production_vertex()->add_particle_out(photon);
253  }
254 
255  // Follow the lepton
256  // Assumptions (otherwise this code will break...)
257  // - it can onlhy branch into l -> l + X, where X is not a lepton
258  // -> we can traverse children and immediately follow any lepton,
259  // we won't miss a "second" lepton
260  // -> we can go until we run out of children, the lepton won't decay
261  // or we can go until the status is final, the result should be the same
262  // There's no clear-cut final() method though, so we go through the end and hope!
263  // avoid endless loop
264  bool foundbranch=true;
265  while ( scatteredlepton->children().size() > 0 ){
266  if ( !foundbranch ){
267  throw std::runtime_error ("none of this lepton's children is a lepton.");
268  }
269  foundbranch=false;
270  for ( auto& c : scatteredlepton->children() ){
271  auto pdgl = TDatabasePDG::Instance()->GetParticle( c->pid() );
272  if ( string(pdgl->ParticleClass()) == "Lepton" ){
273  // found the correct branch,
274  // update and break out of for loop,
275  // resume while loop with new candidate
276  scatteredlepton = c;
277  foundbranch=true;
278  break;
279  }
280  }
281  }
282 
283  // Now add all four in the right order
284  HandleHepmcParticle( lepton, hepmcp_index, particleindex, mEvent );
285  HandleHepmcParticle( hadron, hepmcp_index, particleindex, mEvent );
286  HandleHepmcParticle( scatteredlepton, hepmcp_index, particleindex, mEvent );
287  HandleHepmcParticle( photon, hepmcp_index, particleindex, mEvent );
288 
289  // Now go over all vertices and handle the rest.
290  // Note that by default this could double-count what we just did
291  // But the method takes care of this with a lookup table
292  HandleAllVertices(evt, hepmcp_index, particleindex, mEvent);
293 
294  // x-setion etc.
295  UpdateRuninfo( mObjectsToWriteAtTheEnd, evt );
296  } // if ( mEvent.get() )
297 
298  auto finished = FinishEvent(); // 0 is success
299  return (finished==0);
300  } // try
301  catch(std::exception& error) {
302  std::cerr << "Exception building particle: " << error.what() << std::endl;
303  return false;
304  }
305  }
306 
307  template<>
309  {
310  TProcessIdObjectCount objectCount;
311  mEvent.reset(new erhic::EventHepMC());
312  if (!AddParticle()) {
313  mEvent.reset(nullptr);
314  } // if
315 
316  return mEvent.release();
317  }
318  // -----------------------------------------------------------------------
319 
320  void HandleHepmcParticle( const HepMC3::GenParticlePtr& p, std::map < HepMC3::GenParticlePtr, int >& hepmcp_index, int& particleindex, std::unique_ptr<erhic::EventHepMC>& mEvent ){
321  // do nothing if we already used this particle
322  auto it = hepmcp_index.find(p);
323  if ( it != hepmcp_index.end() ) return;
324 
325  // Create the particle
327  auto v = p->production_vertex();
328  TVector3 vertex;
329  if ( v ) vertex = TVector3(v->position().x(),v->position().y(),v->position().z());
330 
331  particle.SetVertex(vertex);
332  // takes care of xv, yv, zv;
333 
334  TLorentzVector lovec(p->momentum().x(),p->momentum().y(),p->momentum().z(),p->momentum().e());
335  particle.Set4Vector(lovec);
336  // takes care of E, px, py, pz, m, and
337  // derived quantities: pt, p, rapidity, eta, theta, phi
338 
339  // fill up the missing parts
340  particle.SetId( p->pid() );
341  particle.SetStatus(p->status());
342 
343  // Index: Runs from 1 to N.
344  particle.SetIndex ( particleindex );
345 
346  // remember this HepMC3::GenParticlePtr <-> index connection
347  hepmcp_index[p] = particleindex;
348 
349  particleindex++;
350 
351  particle.SetEvent(mEvent.get());
352  mEvent->AddLast(&particle);
353  }
354 
355  // -----------------------------------------------------------------------
356  void HandleAllVertices( HepMC3::GenEvent& evt, std::map < HepMC3::GenParticlePtr, int >& hepmcp_index, int& particleindex, std::unique_ptr<erhic::EventHepMC>& mEvent ){
357  // Note that by default this could double-count previous addiions
358  // Instead of trying to find out which vertex to skip, just use the lookup table
359  // (inside HandleHepmcParticle) to avoid this.
360  for (auto& v : evt.vertices() ){
361  for (auto& p : v->particles_out() ) {
362  HandleHepmcParticle( p, hepmcp_index, particleindex, mEvent );
363  }
364  }
365 
366  // Now the map has built up full 1-1 correspondence between all hepmc particles
367  // and the ParticleMC index.
368  // So we can loop over the particles again, find their parents and offspring, and map accordingly.
369  // We explicitly take advantage of particle index = Event entry # +1
370  // If that changes, we'll need to maintain a second map
371  // Note: the beam proton appears twice; that's consistent with the behavior of pythia6
372  for (auto& v : evt.vertices() ){
373  for (auto& p : v->particles_out() ) {
374  // corresponding ParticleMC is at
375  int treeindex = hepmcp_index[p]-1;
376  assert ( treeindex >=0 ); // Not sure if that can happen. If it does, we could probably just continue;
377  auto track = mEvent->GetTrack(treeindex);
378 
379  // collect all parents
380  vector<int> allparents;
381  for (auto& parent : p->parents() ) {
382  allparents.push_back( hepmcp_index[parent] );
383  }
384  // orig and orig1 aren't very intuitively named...
385  // For pythia6, orig is the default, and orig1 seems purely a placeholder
386  // trying to mimick that here.
387  if ( allparents.size() == 1){
388  track->SetParentIndex( allparents.at(0) );
389  }
390  if ( allparents.size() >= 2){
391  // smallest and highest are stored
392  track->SetParentIndex( *min_element(allparents.begin(), allparents.end() ) );
393  track->SetParentIndex1( *max_element(allparents.begin(), allparents.end() ) );
394  }
395 
396  // same for children
397  vector<int> allchildren;
398  for (auto& child : p->children() ) {
399  allchildren.push_back( hepmcp_index[child] );
400  }
401  if ( allchildren.size() == 1){
402  track->SetChild1Index( allchildren.at(0) );
403  }
404  if ( allchildren.size() >= 2){
405  // smallest and highest are stored
406  track->SetChild1Index( *min_element(allchildren.begin(), allchildren.end() ) );
407  track->SetChildNIndex( *max_element(allchildren.begin(), allchildren.end() ) );
408  }
409  }
410  }
411  }
412 
413 
414  // -----------------------------------------------------------------------
415  void UpdateRuninfo( std::vector<VirtualEventFactory::NamedObjects>& mObjectsToWriteAtTheEnd,
416  const HepMC3::GenEvent& evt ){
417  // Wasteful, since it's only written out for the final event, but
418  // this class doesn't know when that happens
419  TString xsec = "";
420  TString xsecerr = "";
421  if ( auto xsecinfo=evt.cross_section() ){
422  // not all generators have this info
423  xsec += xsecinfo->xsec();
424  xsecerr += xsecinfo->xsec_err();
425  }
426  TObjString* Xsec;
427  TObjString* Xsecerr;
428 
429  // // not saved in the examples I used.
430  // TString trials = ""; trials+= evt.cross_section()->get_attempted_events();
431  // TString nevents = ""; nevents += evt.cross_section()->get_accepted_events();
432  // TObjString* Trials;
433  // TObjString* Nevents;
434 
435  if ( mObjectsToWriteAtTheEnd.size() == 0 ){
436  Xsec = new TObjString;
437  Xsecerr = new TObjString;
438  mObjectsToWriteAtTheEnd.push_back ( VirtualEventFactory::NamedObjects( "crossSection", Xsec) );
439  mObjectsToWriteAtTheEnd.push_back ( VirtualEventFactory::NamedObjects( "crossSectionError", Xsecerr) );
440  // Trials = new TObjString;
441  // Nevents = new TObjString;
442  // mObjectsToWriteAtTheEnd.push_back ( VirtualEventFactory::NamedObjects( "nTrials", Trials) );
443  // mObjectsToWriteAtTheEnd.push_back ( VirtualEventFactory::NamedObjects( "nEvents", Nevents) );
444  }
445  Xsec = (TObjString*) mObjectsToWriteAtTheEnd.at(0).second;
446  Xsec->String() = xsec;
447  Xsecerr = (TObjString*) mObjectsToWriteAtTheEnd.at(1).second;
448  Xsecerr->String() = xsecerr;
449  // Trials = (TObjString*) mObjectsToWriteAtTheEnd.at(2).second;
450  // Trials->String() = trials;
451  // Nevents = (TObjString*) mObjectsToWriteAtTheEnd.at(3).second;
452  // Nevents->String() = nevents;
453  }
454 
455 } // namespace erhic
456 
457 namespace {
458 
459  // Need this to generate the CINT code for each version
461 } // namespace