-
jmcarcell authoredc5e1f31c
Geant4ParticleHandler.cpp 28.97 KiB
//==========================================================================
// AIDA Detector description implementation
//--------------------------------------------------------------------------
// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
// All rights reserved.
//
// For the licensing terms see $DD4hepINSTALL/LICENSE.
// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
//
// Author : M.Frank
//
//==========================================================================
// Framework include files
#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>
#include <DDG4/Geant4ParticleInformation.h>
#include <DDG4/Geant4UserParticleHandler.h>
// 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>
// C/C++ include files
#include <set>
#include <algorithm>
using namespace dd4hep::sim;
using PropertyMask = dd4hep::detail::ReferenceBitMask<int>;
/// Standard constructor
Geant4ParticleHandler::Geant4ParticleHandler(Geant4Context* ctxt, const std::string& nam)
: Geant4GeneratorAction(ctxt,nam), Geant4MonteCarloTruth(),
m_ownsParticles(false), m_userHandler(0), m_primaryMap(0)
{
InstanceCount::increment(this);
//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
m_needsControl = true;
}
/// No default constructor
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() {
clear();
detail::releasePtr(m_userHandler);
InstanceCount::decrement(this);
}
/// No assignment operator
Geant4ParticleHandler& Geant4ParticleHandler::operator=(const Geant4ParticleHandler&) {
return *this;
}
/// Adopt the user particle handler
bool Geant4ParticleHandler::adopt(Geant4Action* action) {
if ( action ) {
if ( !m_userHandler ) {
if ( Geant4UserParticleHandler* h = dynamic_cast<Geant4UserParticleHandler*>(action) ) {
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].");
return false;
}
/// Clear particle maps
void Geant4ParticleHandler::clear() {
detail::releaseObjects(m_particleMap);
m_particleMap.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
void Geant4ParticleHandler::mark(const G4Step* step_value, int reason) {
if ( step_value ) {
mark(step_value->GetTrack(),reason);
return;
}
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 G4Step* step_value) {
if ( step_value ) {
mark(step_value->GetTrack());
return;
}
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);
/// 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);
std::string typ = sd ? sd->sensitiveType() : std::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);
clear();
/// Call the user particle handler
if ( m_userHandler ) {
m_userHandler->generate(event, this);
}
}
/// User stepping callback
void Geant4ParticleHandler::step(const G4Step* step_value, G4SteppingManager* mgr) {
typedef std::vector<const G4Track*> _Sec;
++m_currTrack.steps;
if ( (m_currTrack.reason&G4PARTICLE_ABOVE_ENERGY_THRESHOLD) ) {
//
// Tracks below the energy threshold are NOT stored.
// If these tracks produce hits or are selected due to another signature,
// this criterium will anyhow take precedence.
//
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 ) {
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;
// 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;
}
}
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.mask = prim_part->mask;
m_currTrack.status = prim_part->status;
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;
m_currTrack.charge = int(3.0 * h.charge());
}
else {
m_currTrack.id = m_globalParticleID;
m_currTrack.reason = reason;
m_currTrack.mask = 0;
m_currTrack.status = G4PARTICLE_SIM_CREATED;
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();
m_currTrack.daughters.clear();
m_currTrack.pdgID = h.pdgID();
m_currTrack.mass = h.mass();
m_currTrack.charge = int(3.0 * h.charge());
++m_globalParticleID;
}
m_currTrack.steps = 0;
m_currTrack.secondaries = 0;
m_currTrack.g4Parent = h.parent();
m_currTrack.originalG4ID= h.id();
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();
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);
}
}
if ( m_keepAll ) {
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);
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);
}
// These are candidate tracks with a probability to be stored due to their properties:
// - primary particle
// - hits created
// - secondaries
// - above energy threshold
// - to be kept due to creator process
// - to be kept due to user information of type 'Geant4ParticleInformation' stored in the G4Track
//
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;
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.
//
// 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;
// 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 std::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);
}
}
/// Post-event action callback
void Geant4ParticleHandler::endEvent(const G4Event* event) {
int count = 0;
int level = outputLevel();
do {
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();
// 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
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);
if ( iequiv == ie_end ) {
break; // ERROR !! Will be handled by printout below because ipar==end()
}
g4_equiv = (*iequiv).second;
}
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();
int pdg = int(std::abs(def->GetPDGEncoding())+0.1);
if ( pdg != 0 && pdg<36 && !(pdg > 10 && pdg < 17) && pdg != 22 ) {
error("+++ ERROR: Geant4 particle for track:%d last known is:%d -- is gluon or quark!",equiv,g4_equiv);
}
pdg = int(std::abs(p->pdgID)+0.1);
if ( pdg != 0 && pdg<36 && !(pdg > 10 && pdg < 17) && pdg != 22 ) {
error("+++ ERROR(2): Geant4 particle for track:%d last known is:%d -- is gluon or quark!",equiv,g4_equiv);
}
}
else {
error("+++ No Equivalent particle for track:%d last known is:%d",equiv,g4_equiv);
}
}
// (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);
}
}
#endif
m_equivalentTracks = std::move(equivalents);
m_particleMap = std::move(finalParticles);
}
/// 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() {
std::set<int> remove;
/// Need to start from BACK, to clean first the latest produced stuff.
for(ParticleMap::reverse_iterator i=m_particleMap.rbegin(); i!=m_particleMap.rend(); ++i) {
Particle* p = (*i).second;
PropertyMask mask(p->reason);
// 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() ) {
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() ) {
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 mask(p->reason);
PropertyMask status(p->status);
std::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() ) {
++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) ) {
bool in_map = false, in_parent_list = false;
int parent_id = -1;
if( auto eq_it=m_equivalentTracks.find(p->g4Parent); eq_it != m_equivalentTracks.end() ) {
parent_id = (*eq_it).second;
in_map = (j=m_particleMap.find(parent_id)) != m_particleMap.end();
in_parent_list = p->parents.find(parent_id) != p->parents.end();
}
if ( !in_map || !in_parent_list ) {
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);
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);
}
}
}
if ( num_errors > 0 ) {
except("+++ Consistency check failed. Found %d problems.",num_errors);
}
}
void Geant4ParticleHandler::setVertexEndpointBit() {
for( auto& part : m_particleMap ) {
auto* p = part.second;
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);
}
}
}
}