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