Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// $Id: Geant4Field.cpp 888 2013-11-14 15:54:56Z markus.frank@cern.ch $
//====================================================================
// AIDA Detector description implementation for LCD
//--------------------------------------------------------------------
//
// Author : M.Frank
//
//====================================================================
// Framework include files
#include "DD4hep/Printout.h"
#include "DD4hep/Primitives.h"
#include "DD4hep/InstanceCount.h"
#include "DDG4/Geant4TrackHandler.h"
#include "DDG4/Geant4EventAction.h"
#include "DDG4/Geant4TrackingAction.h"
#include "DDG4/Geant4SteppingAction.h"
#include "DDG4/Geant4MonteCarloRecordManager.h"
#include "DDG4/Geant4ParticleHandler.h"
#include "DDG4/Geant4StepHandler.h"
#include "G4TrackingManager.hh"
#include "G4ParticleDefinition.hh"
#include "G4Track.hh"
#include "G4Step.hh"
#include "G4TrackStatus.hh"
#include "G4PrimaryParticle.hh"
#include "G4PrimaryVertex.hh"
#include "G4Event.hh"
#include <set>
#include <stdexcept>
using namespace std;
using DD4hep::InstanceCount;
using namespace DD4hep;
using namespace DD4hep::Simulation;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
static double m_kinEnergyCut = 500e0;
enum { CREATED_HIT = 1<<0,
PRIMARY_PARTICLE = 1<<1,
SECONDARIES = 1<<2,
ENERGY = 1<<3,
KEEP_PROCESS = 1<<4,
KEEP_PARENT = 1<<5,
CREATED_CALORIMETER_HIT = 1<<6,
CREATED_TRACKER_HIT = 1<<7,
LAST_NOTHING = 1<<31
};
template <typename T> class BitMask {
public:
T& refMask;
BitMask(T& m) : refMask(m) {}
T value() const {
return refMask;
}
void set(const T& m) {
refMask |= m;
}
bool isSet(const T& m) {
return (refMask&m) == m;
}
bool testBit(int bit) const {
T m = T(1)<<bit;
return isSet(m);
}
bool isNull() const {
return refMask == 0;
}
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
template <typename T> inline BitMask<T> bitMask(T& m) { return BitMask<T>(m); }
template <typename T> inline BitMask<const T> bitMask(const T& m) { return BitMask<const T>(m); }
/// Copy constructor
Geant4ParticleHandler::Particle::Particle(const Particle& c)
: id(c.id), parent(c.parent), theParent(c.theParent), reason(c.reason),
vx(c.vx), vy(c.vy), vz(c.vz),
px(c.px), py(c.py), pz(c.pz),
energy(c.energy), time(c.time),
process(c.process), definition(c.definition),
daughters(c.daughters)
{
InstanceCount::increment(this);
}
/// Default constructor
Geant4ParticleHandler::Particle::Particle()
: id(0), parent(0), theParent(0), reason(0),
vx(0.0), vy(0.0), vz(0.0),
px(0.0), py(0.0), pz(0.0),
energy(0.0), time(0.0),
process(0), definition(0),
daughters()
{
InstanceCount::increment(this);
}
/// Default destructor
Geant4ParticleHandler::Particle::~Particle() {
InstanceCount::decrement(this);
}
/// Remove daughter from set
void Geant4ParticleHandler::Particle::removeDaughter(int id_daughter) {
set<int>::iterator j = daughters.find(id_daughter);
if ( j != daughters.end() ) daughters.erase(j);
}
/// Standard constructor
Geant4ParticleHandler::Geant4ParticleHandler(Geant4Context* context, const std::string& nam)
: Geant4GeneratorAction(context,nam), Geant4MonteCarloTruth()
{
//generatorAction().adopt(this);
eventAction().callAtBegin(this,&Geant4ParticleHandler::beginEvent);
eventAction().callAtFinal(this,&Geant4ParticleHandler::endEvent);
trackingAction().callAtFinal(this,&Geant4ParticleHandler::end,CallbackSequence::FRONT);
trackingAction().callUpFront(this,&Geant4ParticleHandler::begin,CallbackSequence::FRONT);
steppingAction().call(this,&Geant4ParticleHandler::step);
declareProperty("PrintEndTracking",m_printEndTracking = false);
declareProperty("PrintStartTracking",m_printStartTracking = false);
declareProperty("SaveProcesses",m_processNames);
InstanceCount::increment(this);
}
/// Default destructor
Geant4ParticleHandler::~Geant4ParticleHandler() {
InstanceCount::decrement(this);
}
/// Clear particle maps
void Geant4ParticleHandler::clear() {
for(ParticleMap::iterator i=m_particleMap.begin(); i!=m_particleMap.end(); ++i)
delete (*i).second;
m_particleMap.clear();
m_equivalentTracks.clear();
}
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/// Mark a Geant4 track to be kept for later MC truth analysis
void Geant4ParticleHandler::mark(const G4Track* track, bool created_hit) {
mark(track); // Does all the checking...
if ( created_hit ) {
}
}
/// Store a track produced in a step to be kept for later MC truth analysis
void Geant4ParticleHandler::mark(const G4Step* step, bool created_hit) {
if ( step ) {
mark(step->GetTrack(),created_hit);
return;
}
except("Cannot mark the G4Track if the step-pointer is invalid!", c_name());
}
/// Mark a Geant4 track of the step to be kept for later MC truth analysis
void Geant4ParticleHandler::mark(const G4Step* step) {
if ( step ) {
mark(step->GetTrack());
return;
}
except("Cannot mark the G4Track if the step-pointer is invalid!", c_name());
}
/// Mark a Geant4 track of the step to be kept for later MC truth analysis
void Geant4ParticleHandler::mark(const G4Track* track) {
BitMask<int> mask(m_currTrack.reason);
mask.set(CREATED_HIT);
/// Check if the track origines from the calorimeter.
// If yes, flag it, because it is a candidate fro removal.
G4VPhysicalVolume* vol = track->GetVolume();
if ( strstr(vol->GetName().c_str(),"cal") ) { // just for test!
mask.set(CREATED_CALORIMETER_HIT);
}
else if ( !mask.isSet(CREATED_TRACKER_HIT) ) {
//printout(INFO,name(),"+++ Create TRACKER HIT: id=%d",m_currTrack.id);
mask.set(CREATED_TRACKER_HIT);
}
/// Check the record consistency
void Geant4ParticleHandler::checkConsistency() const {
}
/// Event generation action callback
void Geant4ParticleHandler::operator()(G4Event*) {
typedef Geant4MonteCarloTruth _MC;
printout(INFO,name(),"+++ Add EVENT extension of type Geant4ParticleHandler.....");
context()->event().addExtension((_MC*)this, typeid(_MC), 0);
}
/// Pre-event action callback
void Geant4ParticleHandler::beginEvent(const G4Event* ) {
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
for(ParticleMap::const_iterator i=m_particleMap.begin(); i!=m_particleMap.end(); ++i) {
//Particle* part = (*i).second;
//int equiv_id = part->parent;
}
}
int Geant4ParticleHandler::recombineParents() {
set<int> remove;
/// Need to start from BACK, to clean first the latest produced stuff.
/// Otherwise the daughter list of the earlier produced tracks would not be empty!
for(ParticleMap::reverse_iterator i=m_particleMap.rbegin(); i!=m_particleMap.rend(); ++i) {
Particle* par = (*i).second;
set<int>& daughters = par->daughters;
if ( daughters.size() == 0 ) {
BitMask<const int> mask(par->reason);
int id = par->id;
bool secondaries = mask.isSet(SECONDARIES);
bool tracker_track = mask.isSet(CREATED_TRACKER_HIT);
bool calo_track = mask.isSet(CREATED_CALORIMETER_HIT);
bool hits_produced = mask.isSet(CREATED_HIT);
bool low_energy = !mask.isSet(ENERGY);
bool keep_process = mask.isSet(KEEP_PROCESS);
bool keep_parent = mask.isSet(KEEP_PARENT);
int parent_id = par->parent;
bool remove_me = false;
/// Primary particles MUST be kept!
if ( mask.isSet(PRIMARY_PARTICLE) ) {
continue;
}
else if ( keep_parent ) {
continue;
}
else if ( keep_process ) {
ParticleMap::iterator ip = m_particleMap.find(parent_id);
if ( ip != m_particleMap.end() ) {
Particle* parent_part = (*ip).second;
BitMask<const int> parent_mask(parent_part->reason);
if ( parent_mask.isSet(ENERGY) ) {
parent_part->reason |= KEEP_PARENT;
continue;
}
}
// Low energy stuff. Remove it. Reassign to parent.
//remove_me = true;
}
/// Remove this track if it has not created a hit and the energy is below threshold
if ( mask.isNull() || (secondaries && low_energy && !hits_produced) ) {
remove_me = true;
}
/// Remove this track if the energy is below threshold. Reassign hits to parent.
else if ( !hits_produced && low_energy ) {
remove_me = true;
}
/// Remove this track if the origine is in the calorimeter. Reassign hits to parent.
else if ( !tracker_track && calo_track && low_energy ) {
remove_me = true;
}
else {
//printout(INFO,name(),"+++ Track: %d should be kept for no obvious reason....",id);
}
/// Remove this track from the list and also do the cleanup in the parent's children list
if ( remove_me ) {
ParticleMap::iterator ip = m_particleMap.find(parent_id);
remove.insert(id);
m_equivalentTracks[id] = parent_id;
if ( ip != m_particleMap.end() ) {
Particle* parent_part = (*ip).second;
bitMask(parent_part->reason).set(mask.value());
// Remove track from parent's list of daughters
parent_part->removeDaughter(id);
}
}
}
}
for(set<int>::const_iterator r=remove.begin(); r!=remove.end();++r) {
ParticleMap::iterator ir = m_particleMap.find(*r);
if ( ir != m_particleMap.end() ) {
delete (*ir).second;
m_particleMap.erase(ir);
}
}
printout(INFO,name(),"+++ Size of track container:%d",int(m_particleMap.size()));
return int(remove.size());
}
/// Pre-event action callback
void Geant4ParticleHandler::endEvent(const G4Event* ) {
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
int count = 0;
do {
printout(INFO,name(),"+++ Iteration:%d Tracks:%d Equivalents:%d",++count,m_particleMap.size(),m_equivalentTracks.size());
} while( recombineParents() > 0 );
for(ParticleMap::const_iterator ipar, i=m_particleMap.begin(); i!=m_particleMap.end(); ++i) {
Particle* part = (*i).second;
int equiv_id = part->parent;
if ( equiv_id != 0 ) {
while( (ipar=m_particleMap.find(equiv_id)) == m_particleMap.end() ) {
TrackEquivalents::const_iterator iequiv = m_equivalentTracks.find(equiv_id);
equiv_id = (iequiv == m_equivalentTracks.end()) ? -1 : (*iequiv).second;
if ( equiv_id < 0 ) {
printout(INFO,name(),"+++ No Equivalent particle for track:%d last known is:%d",part->id,equiv_id);
break;
}
}
if ( equiv_id>0 && equiv_id != part->parent ) part->theParent = equiv_id;
}
}
checkConsistency();
printParticles();
}
/// Print record of kept particles
void Geant4ParticleHandler::printParticles() {
char equiv[32];
ParticleMap::const_iterator ipar;
printout(INFO,name(),"+++ MC Particles #Tracks:%6d %-12s Parent%-7s "
"Primary Secondaries Energy %9s Calo Tracker Process",
int(m_particleMap.size()),"ParticleType","","in [MeV]");
for(ParticleMap::const_iterator i=m_particleMap.begin(); i!=m_particleMap.end(); ++i) {
Particle* part = (*i).second;
BitMask<const int> mask(part->reason);
equiv[0] = 0;
if ( part->parent != part->theParent ) {
::snprintf(equiv,sizeof(equiv),"/%d",part->theParent);
}
printout(INFO,name(),"+++ MC Particle TrackID: %6d %-12s %6d%-7s "
"%-7s %-11s %-7s %8.3g %-4s %-7s %-7s [%s/%s]",
part->id,
part->definition->GetParticleName().c_str(),
part->parent,equiv,
yes_no(mask.isSet(PRIMARY_PARTICLE)),
yes_no(mask.isSet(SECONDARIES)),
yes_no(mask.isSet(ENERGY)),
part->energy,
yes_no(mask.isSet(CREATED_CALORIMETER_HIT)),
yes_no(mask.isSet(CREATED_TRACKER_HIT)),
yes_no(mask.isSet(KEEP_PROCESS)),
part->process ? part->process->GetProcessName().c_str() : "???",
part->process ? G4VProcess::GetProcessTypeName(part->process->GetProcessType()).c_str() : ""
);
}
printout(INFO,"Summary","+++ MC Particles #Tracks:%6d %-12s Parent%-7s "
"Primary Secondaries Energy %9s Calo Tracker Process",
int(m_particleMap.size()),"ParticleType","","in [MeV]");
}
/// User stepping callback
void Geant4ParticleHandler::step(const G4Step* step, G4SteppingManager* /* mgr */) {
typedef std::vector<const G4Track*> _Sec;
if ( m_currTrack.energy > m_kinEnergyCut ) {
//
// 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->GetSecondaryInCurrentStep();
if ( sec->size() > 0 ) {
bitMask(m_currTrack.reason).set(SECONDARIES);
}
}
}
/// Pre-track action callback
void Geant4ParticleHandler::begin(const G4Track* track) {
Geant4TrackHandler h(track);
double kine = h.kineticEnergy();
G4ThreeVector m = track->GetMomentum();
// Extract here all the information from the start of the tracking action
// which we will need later to create a proper MC particle.
m_currTrack.id = h.id();
m_currTrack.reason = (kine > m_kinEnergyCut) ? ENERGY : 0;
m_currTrack.energy = kine;
m_currTrack.parent = h.parent();
m_currTrack.theParent = h.parent();
m_currTrack.definition = h.trackDef();
m_currTrack.process = h.creatorProcess();
m_currTrack.time = h.globalTime();
m_currTrack.vx = h.vertex().x();
m_currTrack.vy = h.vertex().y();
m_currTrack.vz = h.vertex().z();
m_currTrack.px = m.x();
m_currTrack.py = m.y();
m_currTrack.pz = m.z();
// 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() ) {
bitMask(m_currTrack.reason).set(KEEP_PROCESS);
}
}
static G4PrimaryParticle* primary(int id, const G4Event& evt) {
for(int i=0, ni=evt.GetNumberOfPrimaryVertex(); i<ni; ++i) {
G4PrimaryVertex* v = evt.GetPrimaryVertex(i);
for(int j=0, nj=v->GetNumberOfParticle(); j<nj; ++j) {
G4PrimaryParticle* p = v->GetPrimary(i);
if ( id == p->GetTrackID() ) {
return p;
}
}
}
return 0;
}
/// Post-track action callback
void Geant4ParticleHandler::end(const G4Track* track) {
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
Geant4TrackHandler h(track);
int id = h.id();
BitMask<const int> mask(m_currTrack.reason);
G4PrimaryParticle* prim = primary(id,context()->event().event());
if ( prim ) {
m_currTrack.reason |= (PRIMARY_PARTICLE|ENERGY);
const G4ParticleDefinition* def = m_currTrack.definition;
printout(INFO,name(),"+++ Add Primary particle %d def:%p [%s, %s] reason:%d",
id, def,
def ? def->GetParticleName().c_str() : "UNKNOWN",
def ? def->GetParticleType().c_str() : "UNKNOWN", m_currTrack.reason);
}
if ( !mask.isNull() ) {
// These are candate tracks with a probability to be stored due to their properties:
// - no primary particle
// - no hits created
// - no secondaries
// - not above energy threshold
//
// Create a new MC particle from the current track information saved in the pre-tracking action
Particle* p = m_particleMap[id] = new Particle(m_currTrack);
// Add this track to it's parents list of daughters
ParticleMap::iterator ipar = m_particleMap.find(p->parent);
if ( ipar != m_particleMap.end() ) {
(*ipar).second->daughters.insert(id);
}
#if 0
/// Some printout
const char* hit = mask.isSet(CREATED_HIT) ? "CREATED_HIT" : "";
const char* sec = mask.isSet(SECONDARIES) ? "SECONDARIES" : "";
const char* prim = mask.isSet(PRIMARY_PARTICLE) ? "PRIMARY" : "";
printout(INFO,name(),"+++ Tracking postaction: %d/%d par:%d [%s, %s] created by:%s E:%e state:%s %s %s %s",
id,int(m_particleMap.size()),
h.parent(),h.name().c_str(),h.type().c_str(),h.creatorName().c_str(),
p->energy, h.statusName(), prim, hit, sec);
#endif
}
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.
m_equivalentTracks[id] = h.parent();
}