Newer
Older
Markus Frank
committed
//==========================================================================
Markus Frank
committed
//--------------------------------------------------------------------------
// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
Markus Frank
committed
// All rights reserved.
Markus Frank
committed
// For the licensing terms see $DD4hepINSTALL/LICENSE.
// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
Markus Frank
committed
// Author : M.Frank
//
//==========================================================================
#include <DD4hep/Primitives.h>
#include <DD4hep/InstanceCount.h>
#include <DDG4/Geant4StepHandler.h>
#include <DDG4/Geant4TrackHandler.h>
#include <DDG4/Geant4EventAction.h>
#include <DDG4/Geant4SensDetAction.h>
#include <DDG4/Geant4TrackingAction.h>
#include <DDG4/Geant4SteppingAction.h>
#include <DDG4/Geant4ParticleHandler.h>
Markus Frank
committed
#include <DDG4/Geant4ParticleInformation.h>
#include <DDG4/Geant4UserParticleHandler.h>
Markus Frank
committed
// Geant4 include files
#include <G4Step.hh>
#include <G4Track.hh>
#include <G4Event.hh>
#include <G4TrackStatus.hh>
#include <G4PrimaryVertex.hh>
#include <G4PrimaryParticle.hh>
#include <G4TrackingManager.hh>
#include <G4ParticleDefinition.hh>
#include <CLHEP/Units/SystemOfUnits.h>
Markus Frank
committed
// C/C++ include files
#include <set>
#include <stdexcept>
#include <algorithm>
using namespace dd4hep;
using namespace dd4hep::sim;
typedef detail::ReferenceBitMask<int> PropertyMask;
Markus Frank
committed
Geant4ParticleHandler::Geant4ParticleHandler(Geant4Context* ctxt, const string& nam)
: Geant4GeneratorAction(ctxt,nam), Geant4MonteCarloTruth(),
m_ownsParticles(false), m_userHandler(0), m_primaryMap(0)
//generatorAction().adopt(this);
eventAction().callAtBegin(this, &Geant4ParticleHandler::beginEvent);
eventAction().callAtEnd(this, &Geant4ParticleHandler::endEvent);
trackingAction().callAtFinal(this, &Geant4ParticleHandler::end,CallbackSequence::FRONT);
trackingAction().callUpFront(this, &Geant4ParticleHandler::begin,CallbackSequence::FRONT);
steppingAction().call(this, &Geant4ParticleHandler::step);
m_globalParticleID = 0;
declareProperty("PrintEndTracking", m_printEndTracking = false);
declareProperty("PrintStartTracking", m_printStartTracking = false);
declareProperty("KeepAllParticles", m_keepAll = false);
declareProperty("SaveProcesses", m_processNames);
declareProperty("MinimalKineticEnergy", m_kinEnergyCut = 100e0*CLHEP::MeV);
declareProperty("MinDistToParentVertex", m_minDistToParentVertex = 2.2e-14*CLHEP::mm);//default tolerance for g4ThreeVector isNear
Geant4ParticleHandler::Geant4ParticleHandler()
: Geant4GeneratorAction(0,""), Geant4MonteCarloTruth(),
m_ownsParticles(false), m_userHandler(0), m_primaryMap(0)
{
m_globalParticleID = 0;
declareProperty("PrintEndTracking", m_printEndTracking = false);
declareProperty("PrintStartTracking", m_printStartTracking = false);
declareProperty("KeepAllParticles", m_keepAll = false);
declareProperty("SaveProcesses", m_processNames);
declareProperty("MinimalKineticEnergy", m_kinEnergyCut = 100e0*CLHEP::MeV);
declareProperty("MinDistToParentVertex", m_minDistToParentVertex = 2.2e-14*CLHEP::mm);//default tolerance for g4ThreeVector isNear
m_needsControl = true;
/// Default destructor
Geant4ParticleHandler::~Geant4ParticleHandler() {
InstanceCount::decrement(this);
}
Markus Frank
committed
Geant4ParticleHandler& Geant4ParticleHandler::operator=(const Geant4ParticleHandler&) {
return *this;
bool Geant4ParticleHandler::adopt(Geant4Action* action) {
if ( Geant4UserParticleHandler* h = dynamic_cast<Geant4UserParticleHandler*>(action) ) {
Markus Frank
committed
m_userHandler = h;
m_userHandler->addRef();
return true;
except("Cannot add an invalid user particle handler object [Invalid-object-type].");
except("Cannot add an user particle handler object [Object-exists].");
except("Cannot add an invalid user particle handler object [NULL-object].");
/// Clear particle maps
void Geant4ParticleHandler::clear() {
// m_suspendedPM should already be empty and cleared...
assert(m_suspendedPM.empty() && "There was something wrong with the particle record treatment, please open a bug report!");
m_equivalentTracks.clear();
}
/// Mark a Geant4 track to be kept for later MC truth analysis
void Geant4ParticleHandler::mark(const G4Track* track, int reason) {
if ( track ) {
if ( reason != 0 ) {
PropertyMask(m_currTrack.reason).set(reason);
return;
}
except("Cannot mark the G4Track if the pointer is invalid!");
}
/// Store a track produced in a step to be kept for later MC truth analysis
Markus Frank
committed
void Geant4ParticleHandler::mark(const G4Step* step_value, int reason) {
if ( step_value ) {
mark(step_value->GetTrack(),reason);
except("Cannot mark the G4Track if the step-pointer is invalid!");
}
/// Mark a Geant4 track of the step to be kept for later MC truth analysis
Markus Frank
committed
void Geant4ParticleHandler::mark(const G4Step* step_value) {
if ( step_value ) {
mark(step_value->GetTrack());
except("Cannot mark the G4Track if the step-pointer is invalid!");
}
/// Mark a Geant4 track of the step to be kept for later MC truth analysis
void Geant4ParticleHandler::mark(const G4Track* track) {
PropertyMask mask(m_currTrack.reason);
mask.set(G4PARTICLE_CREATED_HIT);
Markus Frank
committed
/// Check if the track origines from the calorimeter.
// If yes, flag it, because it is a candidate for removal.
G4LogicalVolume* vol = track->GetVolume()->GetLogicalVolume();
G4VSensitiveDetector* g4 = vol->GetSensitiveDetector();
Geant4ActionSD* sd = dynamic_cast<Geant4ActionSD*>(g4);
string typ = sd ? sd->sensitiveType() : string();
if ( typ == "calorimeter" )
mask.set(G4PARTICLE_CREATED_CALORIMETER_HIT);
else if ( typ == "tracker" )
mask.set(G4PARTICLE_CREATED_TRACKER_HIT);
else // Assume by default "tracker"
mask.set(G4PARTICLE_CREATED_TRACKER_HIT);
//Geant4ParticleHandle(&m_currTrack).dump4(outputLevel(),vol->GetName(),"hit created by particle");
}
/// Event generation action callback
void Geant4ParticleHandler::operator()(G4Event* event) {
typedef Geant4MonteCarloTruth _MC;
debug("+++ Event:%d Add EVENT extension of type Geant4ParticleHandler.....",event->GetEventID());
context()->event().addExtension((_MC*)this, false);
/// Call the user particle handler
if ( m_userHandler ) {
m_userHandler->generate(event, this);
}
Markus Frank
committed
void Geant4ParticleHandler::step(const G4Step* step_value, G4SteppingManager* mgr) {
typedef vector<const G4Track*> _Sec;
if ( (m_currTrack.reason&G4PARTICLE_ABOVE_ENERGY_THRESHOLD) ) {
//
// Tracks below the energy threshold are NOT stored.
Markus Frank
committed
// If these tracks produce hits or are selected due to another signature,
// this criterium will anyhow take precedence.
//
Markus Frank
committed
const _Sec* sec=step_value->GetSecondaryInCurrentStep();
if ( not sec->empty() ) {
PropertyMask(m_currTrack.reason).set(G4PARTICLE_HAS_SECONDARIES);
}
}
/// Update of the particle using the user handler
if ( m_userHandler ) {
Markus Frank
committed
m_userHandler->step(step_value, mgr, m_currTrack);
}
/// Pre-track action callback
void Geant4ParticleHandler::begin(const G4Track* track) {
Geant4TrackHandler h(track);
double kine = h.kineticEnergy();
G4ThreeVector mom = h.momentum();
const G4ThreeVector& v = h.vertex();
int reason = (kine > m_kinEnergyCut) ? G4PARTICLE_ABOVE_ENERGY_THRESHOLD : 0;
const G4PrimaryParticle* prim = h.primary();
Particle* prim_part = 0;
Andre Sailer
committed
// if particles are not tracked to the end, we pick up where we stopped previously
if (m_haveSuspended) {
//primary particles are already in the particle map, we don't have to store them in another map
auto existingParticle = m_particleMap.find(h.id());
if(existingParticle != m_particleMap.end()) {
m_currTrack.get_data(*(existingParticle->second));
return;
}
//other particles might not be in the particleMap yet, so we take them from here
existingParticle = m_suspendedPM.find(h.id());
if(existingParticle != m_suspendedPM.end()) {
m_currTrack.get_data(*(existingParticle->second));
// make sure we delete a suspended particle in the map, fill it back later...
delete (*existingParticle).second;
m_suspendedPM.erase(existingParticle);
return;
}
Andre Sailer
committed
}
if ( prim ) {
prim_part = m_primaryMap->get(prim);
if ( !prim_part ) {
except("+++ Tracking preaction: Primary particle without generator particle!");
}
reason |= (G4PARTICLE_PRIMARY|G4PARTICLE_ABOVE_ENERGY_THRESHOLD);
m_particleMap[h.id()] = prim_part->addRef();
}
if ( prim_part ) {
m_currTrack.id = prim_part->id;
m_currTrack.reason = prim_part->reason|reason;
m_currTrack.status = prim_part->status;
Markus Frank
committed
m_currTrack.genStatus = prim_part->genStatus;
m_currTrack.spin[0] = prim_part->spin[0];
m_currTrack.spin[1] = prim_part->spin[1];
m_currTrack.spin[2] = prim_part->spin[2];
m_currTrack.colorFlow[0] = prim_part->colorFlow[0];
m_currTrack.colorFlow[1] = prim_part->colorFlow[1];
m_currTrack.parents = prim_part->parents;
m_currTrack.daughters = prim_part->daughters;
m_currTrack.pdgID = prim_part->pdgID;
m_currTrack.mass = prim_part->mass;
Markus Frank
committed
m_currTrack.charge = int(3.0 * h.charge());
}
else {
m_currTrack.id = m_globalParticleID;
m_currTrack.reason = reason;
m_currTrack.status = G4PARTICLE_SIM_CREATED;
Markus Frank
committed
m_currTrack.genStatus = 0;
m_currTrack.spin[0] = 0;
m_currTrack.spin[1] = 0;
m_currTrack.spin[2] = 0;
m_currTrack.colorFlow[0] = 0;
m_currTrack.colorFlow[1] = 0;
m_currTrack.parents.clear();
Markus Frank
committed
m_currTrack.daughters.clear();
m_currTrack.pdgID = h.pdgID();
m_currTrack.mass = h.mass();
Markus Frank
committed
m_currTrack.charge = int(3.0 * h.charge());
++m_globalParticleID;
}
Markus Frank
committed
m_currTrack.steps = 0;
m_currTrack.secondaries = 0;
m_currTrack.g4Parent = h.parent();
m_currTrack.originalG4ID= h.id();
Markus Frank
committed
m_currTrack.process = h.creatorProcess();
m_currTrack.time = h.globalTime();
m_currTrack.vsx = v.x();
m_currTrack.vsy = v.y();
m_currTrack.vsz = v.z();
m_currTrack.vex = 0.0;
m_currTrack.vey = 0.0;
m_currTrack.vez = 0.0;
m_currTrack.psx = mom.x();
m_currTrack.psy = mom.y();
m_currTrack.psz = mom.z();
Markus Frank
committed
m_currTrack.pex = 0.0;
m_currTrack.pey = 0.0;
m_currTrack.pez = 0.0;
// If the creator process of the track is in the list of process products to be kept, set the proper flag
if ( m_currTrack.process ) {
Processes::iterator i=find(m_processNames.begin(),m_processNames.end(),m_currTrack.process->GetProcessName());
if ( i != m_processNames.end() ) {
PropertyMask(m_currTrack.reason).set(G4PARTICLE_KEEP_PROCESS);
}
}
Markus Frank
committed
PropertyMask(m_currTrack.reason).set(G4PARTICLE_KEEP_ALWAYS);
/// Initial update of the particle using the user handler
if ( m_userHandler ) {
m_userHandler->begin(track, m_currTrack);
}
}
/// Post-track action callback
void Geant4ParticleHandler::end(const G4Track* track) {
Geant4TrackHandler h(track);
Geant4ParticleHandle ph(&m_currTrack);
Andre Sailer
committed
const int g4_id = h.id();
int track_reason = m_currTrack.reason;
PropertyMask mask(m_currTrack.reason);
// Update vertex end point and final momentum
G4ThreeVector mom = track->GetMomentum();
const G4ThreeVector& pos = track->GetPosition();
ph->pex = mom.x();
ph->pey = mom.y();
ph->pez = mom.z();
ph->vex = pos.x();
ph->vey = pos.y();
ph->vez = pos.z();
// Set the simulator status bits
PropertyMask simStatus(m_currTrack.status);
// check if the last step ended on the worldVolume boundary
const G4Step* theLastStep = track->GetStep();
G4StepPoint* theLastPostStepPoint = NULL;
if(theLastStep) theLastPostStepPoint = theLastStep->GetPostStepPoint();
if( theLastPostStepPoint &&
( theLastPostStepPoint->GetStepStatus() == fWorldBoundary //particle left world volume
//|| theLastPostStepPoint->GetStepStatus() == fGeomBoundary
)
) {
simStatus.set(G4PARTICLE_SIM_LEFT_DETECTOR);
}
if(track->GetKineticEnergy() <= 0.) {
simStatus.set(G4PARTICLE_SIM_STOPPED);
}
/// Final update of the particle using the user handler
if ( m_userHandler ) {
m_userHandler->end(track, m_currTrack);
Markus Frank
committed
// These are candidate tracks with a probability to be stored due to their properties:
// - primary particle
// - hits created
// - secondaries
// - above energy threshold
Markus Frank
committed
// - to be kept due to creator process
Markus Frank
committed
// - to be kept due to user information of type 'Geant4ParticleInformation' stored in the G4Track
Markus Frank
committed
Geant4ParticleInformation* track_info =
dynamic_cast<Geant4ParticleInformation*>(track->GetUserInformation());
if ( !mask.isNull() || track_info ) {
m_equivalentTracks[g4_id] = g4_id;
ParticleMap::iterator ip = m_particleMap.find(g4_id);
if ( mask.isSet(G4PARTICLE_PRIMARY) ) {
ph.dump2(outputLevel()-1,name(),"Add Primary",h.id(),ip!=m_particleMap.end());
}
// Create a new MC particle from the current track information saved in the pre-tracking action
Particle* part = 0;
if ( ip==m_particleMap.end() ) part = m_particleMap[g4_id] = new Particle();
else part = (*ip).second;
Markus Frank
committed
if ( track_info ) {
mask.set(G4PARTICLE_KEEP_USER);
part->extension.reset(track_info->release());
}
part->get_data(m_currTrack);
}
else {
// These are tracks without any special properties.
//
Markus Frank
committed
// We will not store them on the record, but have to memorise the
// track identifier in order to restore the history for the created hits.
int pid = m_currTrack.g4Parent;
m_equivalentTracks[g4_id] = pid;
Markus Frank
committed
// Need to find the last stored particle and OR this particle's mask
// with the mask of the last stored particle
auto iend = m_equivalentTracks.end(), iequiv=m_equivalentTracks.end();
ParticleMap::iterator ip;
for(ip=m_particleMap.find(pid); ip == m_particleMap.end(); ip=m_particleMap.find(pid)) {
if (iequiv=m_equivalentTracks.find(pid); iequiv == iend) break; // ERROR
pid = (*iequiv).second;
}
if ( ip != m_particleMap.end() )
(*ip).second->reason |= track_reason;
else
ph.dumpWithVertex(outputLevel()+3,name(),"FATAL: No real particle parent present");
if(track->GetTrackStatus() == fSuspend) {
m_haveSuspended = true;
//track is already in particle map, we pick it up from there in begin again
if(m_particleMap.find(g4_id) != m_particleMap.end()) return;
//track is not already stored, keep it in special map
auto iPart = m_suspendedPM.emplace(g4_id, new Particle());
(iPart.first->second)->get_data(m_currTrack);
return; // we trust that we eventually return to this function with another status and go on then
}
/// Pre-event action callback
void Geant4ParticleHandler::beginEvent(const G4Event* event) {
Geant4PrimaryInteraction* interaction = context()->event().extension<Geant4PrimaryInteraction>();
info("+++ Event %d Begin event action. Access event related information.",event->GetEventID());
m_primaryMap = context()->event().extension<Geant4PrimaryMap>();
m_globalParticleID = interaction->nextPID();
m_particleMap.clear();
m_equivalentTracks.clear();
/// Call the user particle handler
if ( m_userHandler ) {
m_userHandler->begin(event);
}
/// Debugging: Dump Geant4 particle map
void Geant4ParticleHandler::dumpMap(const char* tag) const {
const string& n = name();
Geant4ParticleHandle::header4(INFO,n,tag);
for(ParticleMap::const_iterator iend=m_particleMap.end(), i=m_particleMap.begin(); i!=iend; ++i) {
Geant4ParticleHandle((*i).second).dump4(INFO,n,tag);
void Geant4ParticleHandler::endEvent(const G4Event* event) {
int level = outputLevel();
if ( level <= VERBOSE ) dumpMap("Particle ");
debug("+++ Iteration:%d Tracks:%d Equivalents:%d",++count,m_particleMap.size(),m_equivalentTracks.size());
} while( recombineParents() > 0 );
if ( level <= VERBOSE ) dumpMap( "Recombined");
// Rebase the simulated tracks, so that they fit to the generator particles
rebaseSimulatedTracks(0);
if ( level <= VERBOSE ) dumpMap( "Rebased ");
// Consistency check....
checkConsistency();
/// Call the user particle handler
if ( m_userHandler ) {
m_userHandler->end(event);
}
setVertexEndpointBit();
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
// Now export the data to the final record.
Geant4ParticleMap* part_map = context()->event().extension<Geant4ParticleMap>();
part_map->adopt(m_particleMap, m_equivalentTracks);
m_primaryMap = 0;
clear();
}
/// Rebase the simulated tracks, so that they fit to the generator particles
void Geant4ParticleHandler::rebaseSimulatedTracks(int ) {
/// No we have to update the map of equivalent tracks and assign the 'equivalentTrack' entry
TrackEquivalents equivalents, orgParticles;
ParticleMap finalParticles;
ParticleMap::const_iterator ipar, iend, i;
int count;
Geant4PrimaryInteraction* interaction = context()->event().extension<Geant4PrimaryInteraction>();
ParticleMap& pm = interaction->particles;
// (1.0) Copy the pre-defined particle mapping for the simulated tracks
// It is assumed the mapping is ZERO based without holes.
for(count = 0, iend=pm.end(), i=pm.begin(); i!=iend; ++i) {
Particle* p = (*i).second;
orgParticles[p->id] = p->id;
finalParticles[p->id] = p;
if ( p->id > count ) count = p->id;
if ( (p->reason&G4PARTICLE_PRIMARY) != G4PARTICLE_PRIMARY ) {
p->addRef();
}
}
// (1.1) Define the new particle mapping for the simulated tracks
for(++count, iend=m_particleMap.end(), i=m_particleMap.begin(); i!=iend; ++i) {
Particle* p = (*i).second;
if ( (p->reason&G4PARTICLE_PRIMARY) != G4PARTICLE_PRIMARY ) {
//if ( orgParticles.find(p->id) == orgParticles.end() ) {
orgParticles[p->id] = count;
finalParticles[count] = p;
p->id = count;
++count;
}
}
// (2) Re-evaluate the corresponding geant4 track equivalents using the new mapping
Markus Frank
committed
for(TrackEquivalents::iterator ie=m_equivalentTracks.begin(),ie_end=m_equivalentTracks.end(); ie!=ie_end; ++ie) {
int g4_equiv = (*ie).first;
while( (ipar=m_particleMap.find(g4_equiv)) == m_particleMap.end() ) {
TrackEquivalents::const_iterator iequiv = m_equivalentTracks.find(g4_equiv);
Markus Frank
committed
if ( iequiv == ie_end ) {
Markus Frank
committed
break; // ERROR !! Will be handled by printout below because ipar==end()
g4_equiv = (*iequiv).second;
}
Markus Frank
committed
TrackEquivalents::mapped_type equiv = (*ie).second;
if ( ipar != m_particleMap.end() ) {
Geant4ParticleHandle p = (*ipar).second;
equivalents[(*ie).first] = p->id; // requires (1) to be filled properly!
const G4ParticleDefinition* def = p.definition();
if ( pdg != 0 && pdg<36 && !(pdg > 10 && pdg < 17) && pdg != 22 ) {
Markus Frank
committed
error("+++ ERROR: Geant4 particle for track:%d last known is:%d -- is gluon or quark!",equiv,g4_equiv);
if ( pdg != 0 && pdg<36 && !(pdg > 10 && pdg < 17) && pdg != 22 ) {
Markus Frank
committed
error("+++ ERROR(2): Geant4 particle for track:%d last known is:%d -- is gluon or quark!",equiv,g4_equiv);
Markus Frank
committed
error("+++ No Equivalent particle for track:%d last known is:%d",equiv,g4_equiv);
Markus Frank
committed
// (3) Compute the particle's parents and daughters.
// Replace the original Geant4 track with the
// equivalent particle still present in the record.
// Note:
// We rely here on the ordering of the particles accoding to their
// Processing by Geant4 to establish mother daughter relationships.
// == > use finalParticles map and NOT m_particleMap.
int equiv_id = -1;
for( auto& part : finalParticles ) {
auto& p = part.second;
if ( p->g4Parent > 0 ) {
TrackEquivalents::iterator iequ = equivalents.find(p->g4Parent);
if ( iequ != equivalents.end() ) {
equiv_id = (*iequ).second;//equivalents[p->g4Parent];
if ( (ipar=finalParticles.find(equiv_id)) != finalParticles.end() ) {
Particle* q = (*ipar).second;
bool prim = (p->reason&G4PARTICLE_PRIMARY) == G4PARTICLE_PRIMARY;
// We assume that the mother daughter relationship
// is filled by the event readers!
if ( !prim ) {
p->parents.insert(q->id);
}
if ( !p->parents.empty() ) {
int parent_id = (*p->parents.begin());
if ( parent_id == q->id )
q->daughters.insert(p->id);
else if ( !prim )
error("+++ Inconsistency in equivalent record! Parent: %d Daughter:%d",q->id, p->id);
}
else {
error("+++ Inconsistency in parent relashionship: %d NO parent!", p->id);
}
continue;
}
error("+++ Inconsistency in particle record: Geant4 parent %d "
"of particle %d not in record of final particles!",
p->g4Parent,p->id);
}
}
#if 0
for(iend=finalParticles.end(), i=finalParticles.begin(); i!=iend; ++i) {
Particle* p = (*i).second;
if ( p->g4Parent > 0 ) {
int parent_id = (*p->parents.begin());
if ( (ipar=finalParticles.find(parent_id)) != finalParticles.end() ) {
Particle* q = (*ipar).second;
// Generator particles have a proper history.
// We only deal with particles, which are not of MC origin.
//p->parents.insert(q->id);
if ( parent_id == q->id )
q->daughters.insert(p->id);
else
error("+++ Inconsistency in equivalent record! Parent: %d Daughter:%d",q->id, p->id);
continue;
error("+++ Inconsistency in particle record: Geant4 parent %d "
"of particle %d not in record of final particles!",
p->g4Parent,p->id);
}
}
m_equivalentTracks = equivalents;
m_particleMap = finalParticles;
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
/// Default callback to be answered if the particle should be kept if NO user handler is installed
bool Geant4ParticleHandler::defaultKeepParticle(Particle& particle) {
PropertyMask mask(particle.reason);
bool secondaries = mask.isSet(G4PARTICLE_HAS_SECONDARIES);
bool tracker_track = mask.isSet(G4PARTICLE_CREATED_TRACKER_HIT);
bool calo_track = mask.isSet(G4PARTICLE_CREATED_CALORIMETER_HIT);
bool hits_produced = mask.isSet(G4PARTICLE_CREATED_HIT);
bool low_energy = !mask.isSet(G4PARTICLE_ABOVE_ENERGY_THRESHOLD);
/// Remove this track if it has not created a hit and the energy is below threshold
if ( mask.isNull() || (secondaries && low_energy && !hits_produced) ) {
return true;
}
/// Remove this track if the energy is below threshold. Reassign hits to parent.
else if ( !hits_produced && low_energy ) {
return true;
}
/// Remove this track if the origine is in the calorimeter. Reassign hits to parent.
else if ( !tracker_track && calo_track && low_energy ) {
return true;
}
else {
//printout(INFO,name(),"+++ Track: %d should be kept for no obvious reason....",id);
}
return false;
}
/// Clean the monte carlo record. Remove all unwanted stuff.
/// This is the core of the object executed at the end of each event action.
int Geant4ParticleHandler::recombineParents() {
set<int> remove;
/// Need to start from BACK, to clean first the latest produced stuff.
Markus Frank
committed
for(ParticleMap::reverse_iterator i=m_particleMap.rbegin(); i!=m_particleMap.rend(); ++i) {
Particle* p = (*i).second;
// Allow the user to force the particle handling either by
// or the reason mask with G4PARTICLE_KEEP_USER or
// to set the reason mask to NULL in order to drop it.
//
// If the mask entry is set to G4PARTICLE_FORCE_KILL
// or is set to NULL, the particle is ALWAYS removed
//
// Note: This may override all other decisions!
bool remove_me = m_userHandler ? m_userHandler->keepParticle(*p) : defaultKeepParticle(*p);
// Now look at the property mask of the particle
if ( mask.isNull() || mask.isSet(G4PARTICLE_FORCE_KILL) ) {
remove_me = true;
}
else if ( mask.isSet(G4PARTICLE_KEEP_USER) ) {
/// If user decides it must be kept, it MUST be kept!
mask.set(G4PARTICLE_KEEP_USER);
continue;
}
else if ( mask.isSet(G4PARTICLE_PRIMARY) ) {
/// Primary particles MUST be kept!
continue;
}
else if ( mask.isSet(G4PARTICLE_KEEP_ALWAYS) ) {
continue;
}
else if ( mask.isSet(G4PARTICLE_KEEP_PARENT) ) {
//continue;
}
else if ( mask.isSet(G4PARTICLE_KEEP_PROCESS) ) {
if(ParticleMap::iterator ip = m_particleMap.find(p->g4Parent); ip != m_particleMap.end() ) {
Markus Frank
committed
Particle* parent_part = (*ip).second;
PropertyMask parent_mask(parent_part->reason);
if ( parent_mask.isSet(G4PARTICLE_ABOVE_ENERGY_THRESHOLD) ) {
parent_mask.set(G4PARTICLE_KEEP_PARENT);
continue;
}
// Low energy stuff. Remove it. Reassign to parent.
//remove_me = true;
}
/// Remove this track from the list and also do the cleanup in the parent's children list
if ( remove_me ) {
int g4_id = (*i).first;
remove.insert(g4_id);
m_equivalentTracks[g4_id] = p->g4Parent;
if(ParticleMap::iterator ip = m_particleMap.find(p->g4Parent); ip != m_particleMap.end() ) {
Markus Frank
committed
Particle* parent_part = (*ip).second;
PropertyMask(parent_part->reason).set(mask.value());
parent_part->steps += p->steps;
parent_part->secondaries += p->secondaries;
/// Update of the particle using the user handler
if ( m_userHandler ) {
m_userHandler->combine(*p, *parent_part);
}
for( int r : remove ) {
if( auto ir = m_particleMap.find(r); ir != m_particleMap.end() ) {
(*ir).second->release();
m_particleMap.erase(ir);
}
}
return int(remove.size());
/// Check the record consistency
void Geant4ParticleHandler::checkConsistency() const {
int num_errors = 0;
/// First check the consistency of the particle map itself
for(const auto& part : m_particleMap ) {
Geant4Particle* particle = part.second;
Geant4ParticleHandle p(particle);
PropertyMask status(p->status);
set<int>& daughters = p->daughters;
ParticleMap::const_iterator j;
// For all particles, the set of daughters must be contained in the record.
for( int id_dau : daughters ) {
if ( j=m_particleMap.find(id_dau); j == m_particleMap.end() ) {
Markus Frank
committed
++num_errors;
error("+++ Particle:%d Daughter %d is not in particle map!",p->id,id_dau);
// We assume that particles from the generator have consistent parents
// For all other particles except the primaries, the parent must be contained in the record.
if ( !mask.isSet(G4PARTICLE_PRIMARY) && !status.anySet(G4PARTICLE_GEN_STATUS) ) {
int parent_id = -1;
if( auto eq_it=m_equivalentTracks.find(p->g4Parent); eq_it != m_equivalentTracks.end() ) {
Markus Frank
committed
parent_id = (*eq_it).second;
in_map = (j=m_particleMap.find(parent_id)) != m_particleMap.end();
Markus Frank
committed
in_parent_list = p->parents.find(parent_id) != p->parents.end();
if ( !in_map || !in_parent_list ) {
Markus Frank
committed
char parent_list[1024];
parent_list[0] = 0;
++num_errors;
p.dumpWithMomentum(ERROR,name(),"INCONSISTENCY");
for( int ip : p->parents )
::snprintf(parent_list+strlen(parent_list),sizeof(parent_list)-strlen(parent_list),"%d ",ip);
Markus Frank
committed
error("+++ Particle:%d Parent %d (G4id:%d) In record:%s In parent list:%s [%s]",
p->id,parent_id,p->g4Parent,yes_no(in_map),yes_no(in_parent_list),parent_list);
except("+++ Consistency check failed. Found %d problems.",num_errors);
void Geant4ParticleHandler::setVertexEndpointBit() {
for( auto& part : m_particleMap ) {
auto* p = part.second;
Markus Frank
committed
if( !p->parents.empty() ) {
Geant4Particle *parent(m_particleMap[ *p->parents.begin() ]);
const double X( parent->vex - p->vsx );
const double Y( parent->vey - p->vsy );
const double Z( parent->vez - p->vsz );
if( sqrt(X*X + Y*Y + Z*Z) > m_minDistToParentVertex ){
PropertyMask(p->status).set(G4PARTICLE_SIM_PARENT_RADIATED);
}