LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
ParticleListAction.cxx
Go to the documentation of this file.
1 
10 #include "lardataobj/Simulation/sim.h" // sim::NoParticleId
12 
14 
15 #include "Geant4/G4Event.hh"
16 //#include <G4RunManager.hh>
17 #include "Geant4/G4Track.hh"
18 #include "Geant4/G4ThreeVector.hh"
19 #include "Geant4/G4ParticleDefinition.hh"
20 #include "Geant4/G4PrimaryParticle.hh"
21 #include "Geant4/G4DynamicParticle.hh"
22 #include "Geant4/G4VUserPrimaryParticleInformation.hh"
23 #include "Geant4/G4Step.hh"
24 #include "Geant4/G4StepPoint.hh"
25 #include "Geant4/G4VProcess.hh"
26 #include "Geant4/G4String.hh"
27 
28 #include <TLorentzVector.h>
29 #include <TString.h>
30 
31 #include <algorithm>
32 
33 //const G4bool debug = false; // unused
34 
35 // Photon variables defined at each step, for use
36 // in temporary velocity bug fix. -wforeman
38 bool entra = true;
39 
40 namespace larg4 {
41 
42  // Initialize static members.
46 
47  //----------------------------------------------------------------------------
48  // Dropped particle test
49 
51  return !p || p->Trajectory().empty();
52  } // ParticleListAction::isDropped()
53 
54 
55  //----------------------------------------------------------------------------
56  // Constructor.
58  bool storeTrajectories,
59  bool keepEMShowerDaughters)
60  : fenergyCut(energyCut * CLHEP::GeV)
61  , fparticleList(0)
62  , fstoreTrajectories(storeTrajectories)
63  , fKeepEMShowerDaughters(keepEMShowerDaughters)
64  {
65  // Create the particle list that we'll (re-)use during the course
66  // of the Geant4 simulation.
68  fParentIDMap.clear();
69  }
70 
71  //----------------------------------------------------------------------------
72  // Destructor.
74  {
75  // Delete anything that we created with "new'.
76  delete fparticleList;
77  }
78 
79  //----------------------------------------------------------------------------
80  // Begin the event
82  {
83  // Clear any previous particle information.
86  fParentIDMap.clear();
88  fCurrentPdgCode = 0;
89  }
90 
91  //-------------------------------------------------------------
92  // figure out the ultimate parentage of the particle with track ID
93  // trackid
94  // assume that the current track id has already been added to
95  // the fParentIDMap
96  int ParticleListAction::GetParentage(int trackid) const
97  {
98  int parentid = sim::NoParticleId;
99 
100  // search the fParentIDMap recursively until we have the parent id
101  // of the first EM particle that led to this one
103  while( itr != fParentIDMap.end() ){
104  LOG_DEBUG("ParticleListAction")
105  << "parentage for " << trackid
106  << " " << (*itr).second;
107 
108  // set the parentid to the current parent ID, when the loop ends
109  // this id will be the first EM particle
110  parentid = (*itr).second;
111  itr = fParentIDMap.find(parentid);
112  }
113  LOG_DEBUG("ParticleListAction") << "final parent ID " << parentid;
114 
115  return parentid;
116  }
117 
118  //----------------------------------------------------------------------------
119  // Create our initial simb::MCParticle object and add it to the sim::ParticleList.
121  {
122  // Particle type.
123  G4ParticleDefinition* particleDefinition = track->GetDefinition();
124  G4int pdgCode = particleDefinition->GetPDGEncoding();
125 
126  // Get Geant4's ID number for this track. This will be the same
127  // ID number that we'll use in the ParticleList.
128  // It is offset by the number of tracks accumulated from the previous Geant4
129  // runs (if any)
130  G4int trackID = track->GetTrackID() + fTrackIDOffset;
131  fCurrentTrackID = trackID;
132  fCurrentPdgCode = pdgCode;
133 
134  // And the particle's parent (same offset as above):
135  G4int parentID = track->GetParentID() + fTrackIDOffset;
136 
137  std::string process_name = "unknown";
138 
139  // Is there an MCTruth object associated with this G4Track? We
140  // have to go up a "chain" of information to find out:
141  const G4DynamicParticle* dynamicParticle = track->GetDynamicParticle();
142  const G4PrimaryParticle* primaryParticle = dynamicParticle->GetPrimaryParticle();
144  if ( primaryParticle != 0 ){
145  const G4VUserPrimaryParticleInformation* gppi = primaryParticle->GetUserInformation();
146  const g4b::PrimaryParticleInformation* ppi = dynamic_cast<const g4b::PrimaryParticleInformation*>(gppi);
147  if ( ppi != 0 ){
148  primaryIndex = ppi->MCParticleIndex();
149 
150  // If we've made it this far, a PrimaryParticleInformation
151  // object exists and we are using a primary particle, set the
152  // process name accordingly
153  process_name = "primary";
154 
155  // primary particles should have parentID = 0, even if there
156  // are multiple MCTruths for this event
157  parentID = 0;
158  } // end else no primary particle information
159  } // Is there a G4PrimaryParticle?
160  // If this is not a primary particle...
161  else{
162  // check if this particle was made in an EM shower, don't put it in the particle
163  // list as we don't care about secondaries, tertiaries, etc for these showers
164  // figure out what process is making this track - skip it if it is
165  // one of pair production, compton scattering, photoelectric effect
166  // bremstrahlung, annihilation, any ionization - who wants to save
167  // a buttload of electrons that arent from a CC interaction?
168  process_name = track->GetCreatorProcess()->GetProcessName();
170  && (process_name.find("conv") != std::string::npos
171  || process_name.find("LowEnConversion") != std::string::npos
172  || process_name.find("Pair") != std::string::npos
173  || process_name.find("compt") != std::string::npos
174  || process_name.find("Compt") != std::string::npos
175  || process_name.find("Brem") != std::string::npos
176  || process_name.find("phot") != std::string::npos
177  || process_name.find("Photo") != std::string::npos
178  || process_name.find("Ion") != std::string::npos
179  || process_name.find("annihil") != std::string::npos)
180  ){
181 
182  // figure out the ultimate parentage of this particle
183  // first add this track id and its parent to the fParentIDMap
184  fParentIDMap[trackID] = parentID;
185 
186  fCurrentTrackID = -1*this->GetParentage(trackID);
187 
188  // check that fCurrentTrackID is in the particle list - it is possible
189  // that this particle's parent is a particle that did not get tracked.
190  // An example is a partent that was made due to muMinusCaptureAtRest
191  // and the daughter was made by the phot process. The parent likely
192  // isn't saved in the particle list because it is below the energy cut
193  // which will put a bogus track id value into the sim::IDE object for
194  // the sim::SimChannel if we don't check it.
197 
198  // clear current particle as we are not stepping this particle and
199  // adding trajectory points to it
201  return;
202 
203  } // end if keeping EM shower daughters
204 
205  // Check the energy of the particle. If it falls below the energy
206  // cut, don't add it to our list.
207  G4double energy = track->GetKineticEnergy();
208  if( energy < fenergyCut ){
210 
211  // do add the particle to the parent id map though
212  // and set the current track id to be it's ultimate parent
213  fParentIDMap[trackID] = parentID;
214 
215  fCurrentTrackID = -1*this->GetParentage(trackID);
216 
217  return;
218  }
219 
220  // check to see if the parent particle has been stored in the particle navigator
221  // if not, then see if it is possible to walk up the fParentIDMap to find the
222  // ultimate parent of this particle. Use that ID as the parent ID for this
223  // particle
224  if( !fparticleList->KnownParticle(parentID) ){
225  // do add the particle to the parent id map
226  // just in case it makes a daughter that we have to track as well
227  fParentIDMap[trackID] = parentID;
228  int pid = this->GetParentage(parentID);
229 
230  // if we still can't find the parent in the particle navigator,
231  // we have to give up
232  if( !fparticleList->KnownParticle(pid) ){
233  LOG_WARNING("ParticleListAction")
234  << "can't find parent id: "
235  << parentID
236  << " in the particle list, or fParentIDMap."
237  << " Make " << parentID << " the mother ID for"
238  << " track ID " << fCurrentTrackID
239  << " in the hope that it will aid debugging.";
240  }
241  else
242  parentID = pid;
243  }
244 
245  }// end if not a primary particle
246 
247  // This is probably the PDG mass, but just in case:
248  double mass = dynamicParticle->GetMass()/CLHEP::GeV;
249 
250  // Create the sim::Particle object.
253  = new simb::MCParticle( trackID, pdgCode, process_name, parentID, mass);
254  fCurrentParticle.truthIndex = primaryIndex;
255 
256  // if we are not filtering, we have a decision already
257  if (!fFilter) fCurrentParticle.keep = true;
258 
259  // Polarization.
260  const G4ThreeVector& polarization = track->GetPolarization();
261  fCurrentParticle.particle->SetPolarization( TVector3( polarization.x(),
262  polarization.y(),
263  polarization.z() ) );
264 
265  // Save the particle in the ParticleList.
267  }
268 
269  //----------------------------------------------------------------------------
270  void ParticleListAction::PostTrackingAction( const G4Track* aTrack)
271  {
272  if (!fCurrentParticle.hasParticle()) return;
273 
274  // if we have found no reason to keep it, drop it!
275  // (we might still need parentage information though)
276  if (!fCurrentParticle.keep) {
278  // after the particle is archived, it is deleted
280  return;
281  }
282 
283  if(aTrack){
284  fCurrentParticle.particle->SetWeight(aTrack->GetWeight());
285  G4String process = aTrack->GetStep()->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName();
287 
288 
289  }
290 
291  // store truth record pointer, only if it is available
292  if (fCurrentParticle.isPrimary()) {
295  }
296 
297  return;
298  }
299 
300 
301  //----------------------------------------------------------------------------
302  // With every step, add to the particle's trajectory.
303  void ParticleListAction::SteppingAction(const G4Step* step)
304  {
305 
306  if ( !fCurrentParticle.hasParticle() ) {
307  return;
308  }
309 
310  // Temporary fix for problem where DeltaTime on the first step
311  // of optical photon propagation is calculated incorrectly. -wforeman
312  globalTime = step->GetTrack()->GetGlobalTime();
313  velocity_G4 = step->GetTrack()->GetVelocity();
314  velocity_step = step->GetStepLength() / step->GetDeltaTime();
315  if ( (step->GetTrack()->GetDefinition()->GetPDGEncoding()==0) &&
316  fabs(velocity_G4 - velocity_step) > 0.0001 ) {
317  // Subtract the faulty step time from the global time,
318  // and add the correct step time based on G4 velocity.
319  step->GetPostStepPoint()->SetGlobalTime(globalTime - step->GetDeltaTime() + step->GetStepLength()/velocity_G4);
320  }
321 
322 
323  // For the most part, we just want to add the post-step
324  // information to the particle's trajectory. There's one
325  // exception: In PreTrackingAction, the correct time information
326  // is not available. So add the correct vertex information here.
327 
329 
330  // Get the pre/along-step information from the G4Step.
331  const G4StepPoint* preStepPoint = step->GetPreStepPoint();
332 
333  const G4ThreeVector position = preStepPoint->GetPosition();
334  G4double time = preStepPoint->GetGlobalTime();
335 
336  // Remember that LArSoft uses cm, ns, GeV.
337  TLorentzVector fourPos(position.x() / CLHEP::cm,
338  position.y() / CLHEP::cm,
339  position.z() / CLHEP::cm,
340  time / CLHEP::ns);
341 
342  const G4ThreeVector momentum = preStepPoint->GetMomentum();
343  const G4double energy = preStepPoint->GetTotalEnergy();
344  TLorentzVector fourMom(momentum.x() / CLHEP::GeV,
345  momentum.y() / CLHEP::GeV,
346  momentum.z() / CLHEP::GeV,
347  energy / CLHEP::GeV);
348 
349  // Add the first point in the trajectory.
350  AddPointToCurrentParticle( fourPos, fourMom, "Start" );
351 
352  } // end if this is the first step
353 
354  // At this point, the particle is being transported through the
355  // simulation. This method is being called for every voxel that
356  // the track passes through, but we don't want to update the
357  // trajectory information if we're just updating voxels. To check
358  // for this, look at the process name for the step, and compare it
359  // against the voxelization process name (set in PhysicsList.cxx).
360  G4String process = step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName();
361  G4bool ignoreProcess = process.contains("LArVoxel") || process.contains("OpDetReadout");
362 
363  LOG_DEBUG("ParticleListAction::SteppingAction")
364  << ": DEBUG - process='"
365  << process << "'"
366  << " ignoreProcess=" << ignoreProcess
367  << " fstoreTrajectories="
369 
370  // We store the initial creation point of the particle
371  // and its final position (ie where it has no more energy, or at least < 1 eV) no matter
372  // what, but whether we store the rest of the trajectory depends
373  // on the process, and on a user switch.
374  if ( fstoreTrajectories && !ignoreProcess ){
375  // Get the post-step information from the G4Step.
376  const G4StepPoint* postStepPoint = step->GetPostStepPoint();
377 
378  const G4ThreeVector position = postStepPoint->GetPosition();
379  G4double time = postStepPoint->GetGlobalTime();
380 
381  // Remember that LArSoft uses cm, ns, GeV.
382  TLorentzVector fourPos( position.x() / CLHEP::cm,
383  position.y() / CLHEP::cm,
384  position.z() / CLHEP::cm,
385  time / CLHEP::ns );
386 
387  const G4ThreeVector momentum = postStepPoint->GetMomentum();
388  const G4double energy = postStepPoint->GetTotalEnergy();
389  TLorentzVector fourMom( momentum.x() / CLHEP::GeV,
390  momentum.y() / CLHEP::GeV,
391  momentum.z() / CLHEP::GeV,
392  energy / CLHEP::GeV );
393 
394  // Add another point in the trajectory.
395  AddPointToCurrentParticle( fourPos, fourMom, std::string(process) );
396 
397  }
398  }
399 
400  //----------------------------------------------------------------------------
404  : public std::unary_function<sim::ParticleList::value_type, void>
405  {
406  public:
408  : particleList(0)
409  {}
410  void SetParticleList( sim::ParticleList* p ) { particleList = p; }
411  void operator()( sim::ParticleList::value_type& particleListEntry )
412  {
413  // We're looking at this Particle in the list.
414  int particleID = particleListEntry.first;
415 
416  // The parent ID of this particle;
417  // we ask the particle list since the particle itself might have been lost
418  // ("archived"), but the particle list still holds the information we need
419  int parentID = particleList->GetMotherOf(particleID);
420 
421  // If the parentID <= 0, this is a primary particle.
422  if ( parentID <= 0 ) return;
423 
424  // If we get here, this particle is somebody's daughter. Add
425  // it to the list of daughter particles for that parent.
426 
427  // Get the parent particle from the list.
428  sim::ParticleList::iterator parentEntry = particleList->find( parentID );
429 
430  if ( parentEntry == particleList->end() ){
431  // We have an "orphan": a particle whose parent isn't
432  // recorded in the particle list. This is not signficant;
433  // it's possible for a particle not to be saved in the list
434  // because it failed an energy cut, but for it to have a
435  // daughter that passed the cut (e.g., a nuclear decay).
436  return;
437  }
438  if ( !parentEntry->second ) return; // particle archived, nothing to update
439 
440  // Add the current particle to the daughter list of the
441  // parent.
442  simb::MCParticle* parent = (*parentEntry).second;
443  parent->AddDaughter( particleID );
444  }
445  private:
447  };
448 
449  //----------------------------------------------------------------------------
450  // There's one last thing to do: All the particles have their
451  // parent IDs set (in PostTrackingAction), but we haven't set the
452  // daughters yet. That's done in this method.
454  {
455  // Set up the utility class for the "for_each" algorithm. (We only
456  // need a separate set-up for the utility class because we need to
457  // give it the pointer to the particle list. We're using the STL
458  // "for_each" instead of the C++ "for loop" because it's supposed
459  // to be faster.
460  UpdateDaughterInformation updateDaughterInformation;
461  updateDaughterInformation.SetParticleList( fparticleList );
462 
463  // Update the daughter information for each particle in the list.
464  std::for_each(fparticleList->begin(),
465  fparticleList->end(),
466  updateDaughterInformation);
467  }
468 
469  //----------------------------------------------------------------------------
470  // Returns the ParticleList accumulated during the current event.
472  {
473  // check if the ParticleNavigator has entries, and if
474  // so grab the highest track id value from it to
475  // add to the fTrackIDOffset
476  int highestID = 0;
477  for( auto pn = fparticleList->begin(); pn != fparticleList->end(); pn++)
478  if( (*pn).first > highestID ) highestID = (*pn).first;
479 
480  //Only change the fTrackIDOffset if there is in fact a particle to add to the event
481  if( (fparticleList->size())!=0){ fTrackIDOffset = highestID + 1; }
482 
483  return fparticleList;
484  }
485 
486  //----------------------------------------------------------------------------
488  (int trackId) const
489  {
490  auto const iInfo = GetPrimaryTruthMap().find(trackId);
491  return (iInfo == GetPrimaryTruthMap().end())
492  ? simb::NoGeneratedParticleIndex: iInfo->second;
493  } // ParticleListAction::GetPrimaryTruthIndex()
494 
495 
496  //----------------------------------------------------------------------------
497  // Yields the ParticleList accumulated during the current event.
499  {
500  // check if the ParticleNavigator has entries, and if
501  // so grab the highest track id value from it to
502  // add to the fTrackIDOffset
503  int highestID = 0;
504  for( auto pn = fparticleList->begin(); pn != fparticleList->end(); pn++)
505  if( (*pn).first > highestID ) highestID = (*pn).first;
506 
507  //Only change the fTrackIDOffset if there is in fact a particle to add to the event
508  if( (fparticleList->size())!=0 ){ fTrackIDOffset = highestID + 1; }
509 
510  return std::move(*fparticleList);
511  } // ParticleList&& ParticleListAction::YieldList()
512 
513 
514  //----------------------------------------------------------------------------
515  void ParticleListAction::AddPointToCurrentParticle(TLorentzVector const& pos,
516  TLorentzVector const& mom,
517  std::string const& process)
518  {
519 
520  // Add the first point in the trajectory.
521  fCurrentParticle.particle->AddTrajectoryPoint(pos, mom, process);
522 
523  // also see if we can decide to keep the particle
524  if (!fCurrentParticle.keep)
525  fCurrentParticle.keep = fFilter->mustKeep(pos);
526 
527  } // ParticleListAction::AddPointToCurrentParticle()
528 
529 
530  //----------------------------------------------------------------------------
531 
532 } // namespace LArG4
int GetParentage(int trackid) const
unsigned int NumberTrajectoryPoints() const
Definition: MCParticle.h:222
G4bool fstoreTrajectories
Whether to store particle trajectories with each particle.
void AddDaughter(const int trackID)
Definition: MCParticle.h:269
void AddPointToCurrentParticle(TLorentzVector const &pos, TLorentzVector const &mom, std::string const &process)
Adds a trajectory point to the current particle, and runs the filter.
std::unique_ptr< PositionInVolumeFilter > fFilter
filter for particles to be kept
void AddTrajectoryPoint(TLorentzVector const &position, TLorentzVector const &momentum)
Definition: MCParticle.h:261
static int fCurrentPdgCode
pdg code of current particle
virtual void BeginOfEventAction(const G4Event *)
G4UserEventAction interfaces.
std::map< int, GeneratedParticleIndex_t > fPrimaryTruthMap
Map: particle track ID -> index of primary information in MC truth.
const simb::MCTrajectory & Trajectory() const
Definition: MCParticle.h:257
GeneratedParticleIndex_t truthIndex
Index of the particle in the original generator truth record.
list_type::value_type value_type
Definition: ParticleList.h:130
list_type::iterator iterator
Definition: ParticleList.h:131
void Add(simb::MCParticle *value)
Definition: ParticleList.h:315
Geant4 interface.
const sim::ParticleList * GetList() const
void operator()(sim::ParticleList::value_type &particleListEntry)
double velocity_step
GeneratedParticleIndex_t truthInfoIndex() const
Returns the index of the particle in the generator truth record.
sim::ParticleList * fparticleList
ParticleListAction(double energyCut, bool storeTrajectories=false, bool keepEMShowerDaughters=false)
bool empty() const
Definition: MCTrajectory.h:170
std::map< int, int > fParentIDMap
key is current track ID, value is parent ID
int TrackId() const
Definition: MCParticle.h:214
virtual void PreTrackingAction(const G4Track *)
G4UserTrackingAction interfaces.
constexpr GeneratedParticleIndex_t NoGeneratedParticleIndex
Constant representing the absence of generator truth information.
Definition: simb.h:34
void clear()
Resets the information (does not release memory it does not own)
sim::ParticleList && YieldList()
void SetPolarization(const TVector3 &p)
Definition: MCParticle.h:270
Use Geant4&#39;s user "hooks" to maintain a list of particles generated by Geant4.
intermediate_table::const_iterator const_iterator
static bool isDropped(simb::MCParticle const *p)
returns whether the specified particle has been marked as dropped
bool fKeepEMShowerDaughters
whether to keep EM shower secondaries, tertiaries, etc
virtual void PostTrackingAction(const G4Track *)
static const int NoParticleId
Definition: sim.h:28
bool entra
double energy
Definition: plottest35.C:25
iterator begin()
Definition: ParticleList.h:305
void SetParticleList(sim::ParticleList *p)
simb::MCParticle * particle
simple structure representing particle
GeneratedParticleIndex_t GetPrimaryTruthIndex(int trackId) const
Returns the index of primary truth (sim::NoGeneratorIndex if none).
double velocity_G4
void SetWeight(double wt)
Definition: MCParticle.h:272
#define LOG_WARNING(category)
void SetEndProcess(std::string s)
Definition: MCParticle.cxx:105
bool isPrimary() const
Returns whether there is a particle.
double globalTime
#define LOG_DEBUG(id)
bool KnownParticle(int trackID) const
Returns whether we have had this particle, archived or live.
Definition: ParticleList.h:215
std::vector< evd::details::RawDigitInfo_t >::const_iterator end(RawDigitCacheDataClass const &cache)
virtual void EndOfEventAction(const G4Event *)
bool hasParticle() const
Returns whether there is a particle.
Particle list in DetSim contains Monte Carlo particle information.
Float_t track
Definition: plot.C:34
virtual void SteppingAction(const G4Step *)
G4UserSteppingAction interface.
size_type size() const
Definition: ParticleList.h:313
Tools and modules for checking out the basics of the Monte Carlo.
std::map< int, GeneratedParticleIndex_t > const & GetPrimaryTruthMap() const
GeneratedParticleIndex_t MCParticleIndex() const
Returns the index of the corresponding particle in truth record.
void Archive(const key_type &key)
Removes the particle from the list, keeping minimal info of it.
std::size_t GeneratedParticleIndex_t
Type of particle index in the generator truth record (simb::MCTruth).
Definition: simb.h:30