ATLAS Offline Software
Loading...
Searching...
No Matches
PhysicsValidationUserAction.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
3*/
4
5// class header
7
8// ISF includes
10
12
13// ISF Geant4 includes
15
16//Athena includes
20
21// Geant4 includes
22#include "G4ParticleDefinition.hh"
23#include "G4DynamicParticle.hh"
24#include "G4TouchableHistory.hh"
25#include "G4Event.hh"
26#include "G4EventManager.hh"
27#include "G4Step.hh"
28#include "G4TransportationManager.hh"
29
30//External includes
32// ROOT includes
33#include "TTree.h"
34// STL includes
35#include <cmath>
36#include <iostream>
37#include "GaudiKernel/ISvcLocator.h"
38
39namespace G4UA{
40
41 namespace iGeant4 {
42
44 : AthMessaging("PhysicsValidationUserAction")
46 , m_geoIDSvcQuick(nullptr)
47 // branches
48 , m_particles(nullptr)
49 , m_pdg(0)
50 , m_scIn(0)
51 , m_scEnd(0)
52 , m_gen(0)
53 , m_geoID(0)
54 , m_pth(0.)
55 , m_pph(0.)
56 , m_p(0.)
57 , m_eloss(0.)
58 , m_radloss(0.)
59 , m_ionloss(0.)
60 , m_wzOaTr(0), m_thIn(0), m_phIn(0), m_dIn(0)
61 , m_thEnd(0), m_phEnd(0), m_dEnd(0)
62 , m_X0(0), m_L0(0), m_wZ(0), m_dt(0)
63 // more branches
64 , m_interactions(nullptr)
68 , m_p_mother(0), m_radLength(0)
72 {
73 setLevel(m_config.verboseLevel);
74 }
75
77 {
78
79
80 ATH_MSG_VERBOSE("idRmax: "<<m_config.idR <<", idZmax: "<<m_config.idZ);
81 ATH_MSG_VERBOSE("caloR : "<<m_config.caloRmean<<", caloZ : "<<m_config.caloZmean);
82 ATH_MSG_VERBOSE("muonR : "<<m_config.muonRmean<<", muonZ : "<<m_config.muonZmean);
83 ATH_MSG_VERBOSE("muonR : "<<m_config.cavernRmean<<", muonZ : "<<m_config.cavernZmean);
84 ATH_MSG_VERBOSE("cavernR : "<<m_config.cavernRmean<<", cavernZ : "<<m_config.cavernZmean);
85
86 m_currentTrack = -1;
87 m_trackGenMap.clear();
88 //m_idToStackParticleMap.clear();
89
90
91 }
92
94 {
95
96 m_X0=0.;
97 m_L0=0.;
98 m_wZ=0.;
99 m_wzOaTr=0.;
100
101 m_radLength=0;
102
103 return;
104 }
105
107 {
108
109 if (m_config.geoIDSvc.retrieve().isFailure()) {
110 ATH_MSG_FATAL("Could not retrieve ISF GeoID Svc: " << m_config.geoIDSvc);
111 return;
112 }
113
114 m_geoIDSvcQuick = &(*m_config.geoIDSvc);
115
116 // setup for validation mode
117 if ( m_config.validationOutput) {
118
119 // retrieve the histogram service
120 if ( m_config.thistSvc.retrieve().isSuccess() ) {
121 // Create the prefix of histogram names for the THistSvc
122 const char *treeName="particles";
123 const std::string prefix = "/" + m_config.validationStream + "/"+ treeName;
124 m_particles = new TTree( treeName, treeName );
125 m_particles->Branch("pdg" , &m_pdg , "pdg/I" ); // pdg id
126 m_particles->Branch("scIn" , &m_scIn , "scIn/I" ); // input process
127 m_particles->Branch("scOut" , &m_scEnd , "scOut/I" ); // endpoint process
128 m_particles->Branch("gen" , &m_gen , "gen/I" ); // generation (0 for primary)
129 m_particles->Branch("geoID" , &m_geoID , "geoID/I" ); // subdetector id
130 m_particles->Branch("pth" , &m_pth , "pth/F" ); // input momentum polar angle
131 m_particles->Branch("pph" , &m_pph , "pph/F" ); // input momemtum azimuthal angle
132 m_particles->Branch("p" , &m_p , "p/F" ); // input momentum
133 m_particles->Branch("eloss" , &m_eloss , "eloss/F" ); // energy loss
134 m_particles->Branch("radloss" , &m_radloss , "radloss/F" ); // radiative eloss
135 m_particles->Branch("ionloss" , &m_ionloss , "ionloss/F" ); // ionization eloss
136 m_particles->Branch("wzOaTr" , &m_wzOaTr , "wzOaTr/F" ); // zOverZtimesRho times dInX0
137 m_particles->Branch("X0" , &m_X0 , "X0/F" ); // dInX0 (material thickness)
138 m_particles->Branch("L0" , &m_L0 , "L0/F" ); // dInL0
139 m_particles->Branch("wZ" , &m_wZ , "wZ/F" ); // averageZ time dInX0
140 m_particles->Branch("dt" , &m_dt , "dt/F" ); // time interval
141 m_particles->Branch("thIn" , &m_thIn , "thIn/F" ); // polar angle input position
142 m_particles->Branch("phIn" , &m_phIn , "phIn/F" ); // azimuthal angle input position
143 m_particles->Branch("dIn" , &m_dIn , "dIn/F" ); // distance input position
144 m_particles->Branch("thEnd" , &m_thEnd , "thEnd/F" ); // polar angle exit position
145 m_particles->Branch("phEnd" , &m_phEnd , "phEnd/F" ); // azimuthal angle exit position
146 m_particles->Branch("dEnd" , &m_dEnd , "dEnd/F" ); // distance exit position
147
148 // register the Tree to the THistSvc and return it's StatusCode
149 if(m_config.thistSvc->regTree( prefix, m_particles)!=StatusCode::SUCCESS){
150 ATH_MSG_ERROR("Cannot register validation tree");
151 }
152
153 m_X0=0.;
154 m_L0=0.;
155 m_wZ=0.;
156 m_wzOaTr=0.;
157
158 const char *treeNameInt="interactions";
159 const std::string prefixInt = "/" + m_config.validationStream + "/"+ treeNameInt;
160 m_interactions = new TTree( treeNameInt, treeNameInt );
161 m_interactions->Branch("process" , &m_process , "process/I" );
162 m_interactions->Branch("pdg_mother" , &m_pdg_mother , "pdg_mother/I" );
163 m_interactions->Branch("gen_mother" , &m_gen_mother , "gen_mother/I" );
164 m_interactions->Branch("nChild" , &m_nChild , "nch/I" );
165 m_interactions->Branch("vtx_dist" , &m_vtx_dist , "vtx_dist/F" );
166 m_interactions->Branch("vtx_theta" , &m_vtx_theta , "vtx_theta/F" );
167 m_interactions->Branch("vtx_phi" , &m_vtx_phi , "vtx_phi/F" );
168 m_interactions->Branch("vtx_e_diff" , &m_vtx_e_diff , "vtx_e_diff/F" );
169 m_interactions->Branch("vtx_p_diff" , &m_vtx_p_diff , "vtx_p_diff/F" );
170 m_interactions->Branch("vtx_plong_diff" , &m_vtx_plong_diff , "vtx_plong_diff/F" );
171 m_interactions->Branch("vtx_pperp_diff" , &m_vtx_pperp_diff , "vtx_pperp_diff/F" );
172 m_interactions->Branch("radLength" , &m_radLength , "radLength/F" );
173 m_interactions->Branch("p_mother" , &m_p_mother , "p_mother/F" );
174 m_interactions->Branch("pdg_child" , m_pdg_child , "pdg_child[nch]/I" );
175 m_interactions->Branch("fp_child" , m_fp_child , "fp_child[nch]/F" );
176 m_interactions->Branch("oa_child" , m_oa_child , "oa_child[nch]/F" );
177 // register the Tree to the THistSvc and return it's StatusCode
178 if(m_config.thistSvc->regTree( prefixInt, m_interactions)!=StatusCode::SUCCESS){
179 ATH_MSG_ERROR("Cannot register validation tree");
180 }
181
182 m_radLength = 0.;
183
184 }
185 }
186
187
188 // get the geometry manager and check how many layers are present.
189 G4TransportationManager *transportationManager(G4TransportationManager::GetTransportationManager());
190 G4LogicalVolume *world((*(transportationManager->GetWorldsIterator()))->GetLogicalVolume());
191 ATH_MSG_VERBOSE("World G4LogicalVolume Name: " << world->GetName() << " has " << world->GetNoDaughters() << " daughters.");
192 if ("World::World"==world->GetName())
193 {
194 ATH_MSG_INFO("Atlas::Atlas is not the world volume, so assume we are in a cosmics job.");
195 //Cosmics-specific configuration.
198 }
199
200 }
201
203 {
204 //std::cout<<"PhysicsValidationUserAction::SteppingAction"<<std::endl;
205
206 // process identification
207 const G4VProcess * process = aStep->GetPostStepPoint()->GetProcessDefinedStep();
208
209 // material info
210 G4StepPoint *preStep=aStep->GetPreStepPoint();
211 G4StepPoint *postStep=aStep->GetPostStepPoint();
212
213 G4ThreeVector mom = preStep->GetMomentum();
214 const G4ThreeVector& pos = preStep->GetPosition();
215
216 // info about generating particle
217 G4Track * track = aStep->GetTrack();
218
219 int trackID=track->GetTrackID();
220 const bool trackIsAlive = (track->GetTrackStatus() == fAlive || track->GetTrackStatus() == fStopButAlive);
221
222 if (trackID != m_currentTrack) { // for new G4Track only
223
224 m_pdg = track->GetDefinition()->GetPDGEncoding();
225 const G4VProcess* creation = track->GetCreatorProcess();
226 m_scIn = creation? creation->GetProcessSubType() : -1;
227
228 VTrackInformation * trackInfo= static_cast<VTrackInformation*>(track->GetUserInformation());
229 HepMC::GenParticlePtr currentGenParticle= trackInfo ? trackInfo->GetCurrentGenParticle() : nullptr;
230 HepMC::GenVertexPtr vtx = currentGenParticle ? currentGenParticle->production_vertex() : nullptr;
231 m_gen = currentGenParticle? 0 : -1;
232
233 if (currentGenParticle) { // mc truth known
234 while (currentGenParticle && vtx ) {
235 int pdgID=currentGenParticle->pdg_id();
236#ifdef HEPMC3
237 const HepMC::GenParticlePtr genmom = vtx->particles_in().size()>0 ? vtx->particles_in().front() : nullptr;
238 if ( genmom && pdgID!=genmom->pdg_id() ) m_gen++;
239 else if (vtx->particles_out().size()>0 && currentGenParticle!=vtx->particles_out().front()) m_gen++;
240
241#else
242 HepMC::GenParticlePtr genmom = vtx->particles_in_size()>0 ? *(vtx->particles_in_const_begin()) : nullptr;
243 if ( genmom && pdgID!=genmom->pdg_id() ) m_gen++;
244 else if (vtx->particles_out_size()>0 && currentGenParticle!=*(vtx->particles_out_const_begin())) m_gen++;
245#endif
246 vtx = genmom ? genmom->production_vertex() : nullptr;
247 currentGenParticle = genmom;
248 }
249 } else {
250 // retrieve info from parent track
251 int parentID=track->GetParentID();
252 std::map<int, int>::iterator genIt = m_trackGenMap.find(parentID);
253 if ( genIt != m_trackGenMap.end()) m_gen = (genIt->second >= 0) ? genIt->second+1 : genIt->second-1;
254 }
255
256 m_trackGenMap.try_emplace(trackID, m_gen);
257
258 m_currentTrack=trackID;
259
260 m_radLength = 0.;
261 }
262
263 AtlasDetDescr::AtlasRegion geoID = m_geoIDSvcQuick->identifyNextGeoID( pos.x(),
264 pos.y(),
265 pos.z(),
266 mom.x(),
267 mom.y(),
268 mom.z() );
269
270
271
272 double stepLength = aStep->GetStepLength();
273 double radLengthInX0 = preStep->GetMaterial()->GetRadlen();
274 double l0 = preStep->GetMaterial()->GetNuclearInterLength();
275 float stepInX0 = stepLength/radLengthInX0;
276
277 if (stepInX0>1.e-06) {
278
279 m_X0 += stepInX0;
280 m_radLength += stepInX0;
281
282 if (l0>0.) {
283 m_L0 += stepLength/l0;
284 // average Z/A over fraction of atoms rather than weight fraction
285 // const G4double* fVec = preStep->GetMaterial()->GetFractionVector(); // mass fraction
286 double totNA = preStep->GetMaterial()->GetTotNbOfAtomsPerVolume();
287 const G4ElementVector* eVec = preStep->GetMaterial()->GetElementVector();
288 const G4double* atVector = preStep->GetMaterial() ->GetVecNbOfAtomsPerVolume();
289 float mFactor =stepInX0* preStep->GetMaterial()->GetDensity();
290
291 float zOverA = 0.; float frSum = 0.;
292 for (unsigned int i=0; i<eVec->size(); i++) {
293 float fEl = atVector ? atVector[i]/totNA : 0.;
294 m_wZ += stepInX0*fEl*((*eVec)[i]->GetZ());
295 //std::cout <<"elements:"<<i<<","<<fVec[i]<<":"<<(*eVec)[i]->GetZ()<< ","<<m_wZ<<","<<m_wZ/m_X0<<std::endl;
296 //m_wA += stepInX0*fVec[i]*((*eVec)[i]->GetA());
297 zOverA += fEl*((*eVec)[i]->GetZ())/((*eVec)[i]->GetA());
298 frSum += fEl;
299 }
300 if (fabs(frSum-1.)>0.01) ATH_MSG_DEBUG("G4 material description inconsistent, sum of element fractions:"<< frSum);
301 m_wzOaTr += mFactor*zOverA;
302 }
303
304 }
305
306
307 // save interaction info (if any)
308 if ( process && process->GetProcessSubType()>0 && process->GetProcessSubType()!=91) {
309
310 float eloss = postStep->GetMomentum().mag()-preStep->GetMomentum().mag();
311
312 if (process->GetProcessSubType()==2 ) m_ionloss+=eloss;
313 if (process->GetProcessSubType()==3 ) m_radloss+=eloss;
314
315 AtlasG4EventUserInfo* atlasG4EvtUserInfo = static_cast<AtlasG4EventUserInfo*> (G4EventManager::GetEventManager()->GetConstCurrentEvent()->GetUserInformation());
316 VTrackInformation * trackInfo = static_cast<VTrackInformation*>(track->GetUserInformation());
317 ::iGeant4::Geant4TruthIncident truth( aStep, *trackInfo->GetBaseISFParticle(), geoID, atlasG4EvtUserInfo);
318 unsigned int nSec = truth.numberOfChildren();
319 if (nSec>0 || !trackIsAlive ) { // save interaction info
320 //std::cout <<"interaction:"<< process->GetProcessSubType() <<":"<<nSec<< std::endl;
321 m_process=process->GetProcessSubType();
322 m_pdg_mother = track->GetDefinition()->GetPDGEncoding();
324 G4ThreeVector mom = preStep->GetMomentum();
325 m_p_mother = mom.mag();
326
327 m_vtx_dist = postStep->GetPosition().mag();
328 m_vtx_theta = postStep->GetPosition().theta();
329 m_vtx_phi = postStep->GetPosition().phi();
330
331 int iPrimSurv = !trackIsAlive ? 0 : 1;
332 m_nChild = nSec+iPrimSurv;
333
334 G4ThreeVector pbal(mom);
335
336 if (iPrimSurv>0) {
338 m_fp_child[0] = postStep->GetMomentum().mag()/m_p_mother;
339 m_oa_child[0] = mom*postStep->GetMomentum()/m_p_mother/postStep->GetMomentum().mag();
340 pbal -= postStep->GetMomentum();
341 }
342
343 unsigned int nSecMax = nSec+iPrimSurv> MAXCHILDREN ? MAXCHILDREN-iPrimSurv : nSec;
344 for (unsigned int isec=0; isec< nSec; isec++) {
345 G4ThreeVector secMom = truth.childP(isec);
346 if (isec<nSecMax) {
347 m_pdg_child[isec+iPrimSurv] = truth.childPdgCode(isec);
348 m_fp_child[isec+iPrimSurv] = secMom.mag()/m_p_mother;
349 m_oa_child[isec+iPrimSurv] = secMom*mom/m_p_mother/secMom.mag();
350 }
351 pbal -= secMom;
352 }
353
354 m_vtx_p_diff = pbal.mag();
355 m_vtx_plong_diff = pbal*mom/m_p_mother;
357
358 m_interactions->Fill();
359
360 // reset the radiation length
361 if (m_process==3) m_radLength = 0.;
362 }
363 }
364
365 // crossing subdetector boundary ?
366 G4VPhysicalVolume *preVol=preStep->GetPhysicalVolume();
367 G4VPhysicalVolume *postVol=postStep->GetPhysicalVolume();
368
369 if (postVol==0) {
370 // left world -save info
371 m_scEnd = 0;
372 m_geoID = geoID;
373 m_dt = track->GetLocalTime();
374
375 m_pth = track->GetVertexMomentumDirection().theta();
376 m_pph = track->GetVertexMomentumDirection().phi();
377 m_p = track->GetVertexKineticEnergy();
378 m_eloss = track->GetKineticEnergy()-m_p;
379
380 m_thIn= track->GetVertexPosition().theta();
381 m_phIn= track->GetVertexPosition().phi();
382 m_dIn= track->GetVertexPosition().mag();
383
384 m_thEnd=postStep->GetPosition().theta();
385 m_phEnd=postStep->GetPosition().phi();
386 m_dEnd=postStep->GetPosition().mag();
387
388 m_particles->Fill();
389 m_X0 = 0.;
390 m_L0 = 0.;
391 m_wZ = 0.;
392 m_wzOaTr = 0.;
393
394 m_radloss = 0.;
395 m_ionloss = 0.;
396
397 return;
398 }
399
400 // if particle killed, save the info
401 if ( !trackIsAlive ) {
402 m_scEnd = process? process->GetProcessSubType() : -1;
403 m_geoID = geoID;
404 m_dt = track->GetLocalTime();
405
406 m_pth = track->GetVertexMomentumDirection().theta();
407 m_pph = track->GetVertexMomentumDirection().phi();
408 m_p = track->GetVertexKineticEnergy();
409 m_eloss = track->GetKineticEnergy()-m_p;
410
411 m_thIn= track->GetVertexPosition().theta();
412 m_phIn= track->GetVertexPosition().phi();
413 m_dIn= track->GetVertexPosition().mag();
414
415 m_thEnd=postStep->GetPosition().theta();
416 m_phEnd=postStep->GetPosition().phi();
417 m_dEnd=postStep->GetPosition().mag();
418
419 m_particles->Fill();
420 m_X0 = 0.;
421 m_L0 = 0.;
422 m_wZ = 0.;
423 m_radloss = 0.;
424 m_ionloss = 0.;
425 m_wzOaTr = 0.;
426 m_radLength = 0.;
427 }
428
429 if ( preVol==postVol ) return; // assume boundaries do not cross G4Volumes
430
431 // Detector boundaries defined by central GeoIDSvc
432 const G4ThreeVector &postPos = postStep->GetPosition();
433 const G4ThreeVector &postMom = postStep->GetMomentum();
434 AtlasDetDescr::AtlasRegion nextGeoID = m_geoIDSvcQuick->identifyNextGeoID( postPos.x(),
435 postPos.y(),
436 postPos.z(),
437 postMom.x(),
438 postMom.y(),
439 postMom.z() );
440
441 // save info if leaving the subdetector
442 if ( nextGeoID == geoID) {
443 ATH_MSG_DEBUG("track stays inside "<<geoID);
444 } else {
445 ATH_MSG_DEBUG("track moves from "<<geoID<<" to "<<nextGeoID);
446
447 // Don't save if doing a zero step ?
448 //if (aStep->GetTrack()->GetTrackLength() == 0.) {
449 if (aStep->GetStepLength() == 0.) {
450 return;
451 }
452
453 // don't change geometry assignment for validation ntuple
454 m_geoID = geoID;
455
456 //4ParticleDefinition* particleDefinition = track->GetDefinition();
457
458 const G4ThreeVector& g4pos = track->GetPosition();
459 //const double gTime = track->GetGlobalTime();
460 const HepGeom::Point3D<double> position(g4pos.x(),g4pos.y(),g4pos.z());
461
462 G4ThreeVector g4mom = track->GetMomentum();
463 const HepGeom::Vector3D<double> momentum(g4mom.x(),g4mom.y(),g4mom.z());
464
465 bool dead=false;
466 if (track->GetTrackStatus()==fStopAndKill) {
467 dead=true;
468 }
469
470 if (!dead) {
471
472 // track info
473 //VTrackInformation * trackInfo= static_cast<VTrackInformation*>(track->GetUserInformation());
474 m_scEnd = 0;
475 m_dt = track->GetLocalTime();
476
477 m_pth = track->GetVertexMomentumDirection().theta();
478 m_pph = track->GetVertexMomentumDirection().phi();
479 m_p = track->GetVertexKineticEnergy();
480 m_eloss = track->GetKineticEnergy()-m_p;
481
482 m_thIn= track->GetVertexPosition().theta();
483 m_phIn= track->GetVertexPosition().phi();
484 m_dIn= track->GetVertexPosition().mag();
485
486 m_thEnd=postStep->GetPosition().theta();
487 m_phEnd=postStep->GetPosition().phi();
488 m_dEnd=postStep->GetPosition().mag();
489
490 m_particles->Fill();
491 m_X0 = 0.;
492 m_L0 = 0.;
493 m_wZ = 0.;
494 m_radloss = 0.;
495 m_ionloss = 0.;
496 m_wzOaTr= 0.;
497 }
498 m_X0=0.;
499
500 }
501
502 }
503
505 {
506 return;
507 }
508
509 } // namespace iGeant4
510
511} // namespace G4UA
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_DEBUG(x)
#define MAXCHILDREN
void setLevel(MSG::Level lvl)
Change the current logging level.
AthMessaging(IMessageSvc *msgSvc, const std::string &name)
Constructor.
This class is attached to G4Event objects as UserInformation.
TTree * m_particles
ROOT tree containing track info.
virtual void BeginOfEventAction(const G4Event *) override final
virtual void UserSteppingAction(const G4Step *) override final
TTree * m_interactions
ROOT tree containing vertex info.
ISF::IGeoIDSvc * m_geoIDSvcQuick
access to the central ISF GeoID serice
virtual void PreUserTrackingAction(const G4Track *) override final
virtual void EndOfEventAction(const G4Event *) override final
virtual void BeginOfRunAction(const G4Run *) override final
unsigned short numberOfChildren() const
Return total number of child particles.
Instances of classes derived from this class are attached as UserInformation to G4Tracks.
ISF_Geant4 specific implementation of the ISF::ITruthIncident.
int childPdgCode(unsigned short index) const override final
Return the PDG Code of the i-th child particle.
const G4ThreeVector childP(unsigned short index) const
Return p of the i-th child particle.
const std::string process
AtlasRegion
A simple enum of ATLAS regions and sub-detectors.
Definition AtlasRegion.h:21
HepMC::GenVertex * GenVertexPtr
Definition GenVertex.h:59
GenParticle * GenParticlePtr
Definition GenParticle.h:37