ATLAS Offline Software
Loading...
Searching...
No Matches
PunchThroughG4Tool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
5// PunchThrough headers
9
10// PathResolver
12
13// standard C++ libraries
14#include <iostream>
15#include <sstream>
16#include <string>
17#include <algorithm>
18#include <vector>
19#include <numeric>
20#include <string_view>
21#include <charconv>
22
23// standard C libraries
24#include <cmath>
25
26// CLHEP
27#include "CLHEP/Random/RandFlat.h"
28
29// ROOT
30#include "TFile.h"
31#include "TH2F.h"
32#include "TH1.h"
33#include "TMath.h"
34#include "TKey.h"
35
36// namespace Amg functions
38
39PunchThroughG4Tool::PunchThroughG4Tool(const std::string& type, const std::string& name, const IInterface* parent)
40 : base_class(type,name,parent)
41{
42}
43
44// Athena method, used to get out the G4 geometry and set up the Fast Simulation Models
46 ATH_MSG_DEBUG("[PunchThroughG4Tool] ==>" << name() << "::initialize()");
47
48 //retrieve inverse CDF config file
50 {
51 ATH_MSG_WARNING("[PunchThroughG4Tool] unable to open or read the inverse CDF config");
52 return StatusCode::FAILURE;
53 }
54
55 //retrieve inverse PCA config file
57 {
58 ATH_MSG_WARNING("[PunchThroughG4Tool] unable to open or read the inverse PCA config");
59 return StatusCode::FAILURE;
60 }
61
62 //check first the size of infoMap for both PCA and CDF, they should be equal
63 if (!(m_xml_info_pca.size() == m_xml_info_cdf.size()))
64 {
65 ATH_MSG_WARNING("[PunchThroughG4Tool] size of infoMap for PCA and CDF differs! Something is wrong with input xml files.");
66 return StatusCode::FAILURE;
67 }
68
69 ATH_MSG_INFO( "[PunchThroughG4Tool] initialization is successful" );
70 return StatusCode::SUCCESS;
71}
72
74 G4ParticleTable *ptable = G4ParticleTable::GetParticleTable();
75 for ( unsigned int num = 0; num < m_punchThroughParticles.size(); num++ )
76 {
77 const int pdg = m_punchThroughParticles[num];
78 // if no information is given on the creation of anti-particles -> do not simulate anti-particles
79 const bool doAnti = ( num < m_doAntiParticles.size() ) ? m_doAntiParticles[num] : false;
80 // if no information is given on the minimum energy -> take 50. MeV as default
81 const double minEnergy = ( num < m_minEnergy.size() ) ? m_minEnergy[num] : 50.;
82 // if no information is given on the maximum number of punch-through particles -> take -1 as default
83 const int maxNum = ( num < m_minEnergy.size() ) ? m_maxNumParticles[num] : -1;
84 // if no information is given on the scale factor for the number of particles -> take 1. as defaulft
85 const double numFactor = ( num < m_numParticlesFactor.size() ) ? m_numParticlesFactor[num] : 1.;
86 // if no information is given on the position angle factor -> take 1.
87 const double posAngleFactor = ( num < m_posAngleFactor.size() ) ? m_posAngleFactor[num] : 1.;
88 // if no information is given on the momentum angle factor -> take 1.
89 const double momAngleFactor = ( num < m_momAngleFactor.size() ) ? m_momAngleFactor[num] : 1.;
90 // if no information is given on the scale factor for the energy -> take 1. as default
91 const double energyFactor = ( num < m_energyFactor.size() ) ? m_energyFactor[num] : 1.;
92
93 // register the particle
94 ATH_MSG_VERBOSE("VERBOSE: [PunchThroughG4Tool] registering punch-through particle type with pdg = " << pdg );
95 if (registerPunchThroughParticle( *ptable, pdg, doAnti, minEnergy, maxNum, numFactor, energyFactor, posAngleFactor, momAngleFactor ) != StatusCode::SUCCESS)
96 {
97 ATH_MSG_ERROR("[PunchThroughG4Tool] unable to register punch-through particle type with pdg = " << pdg);
98 return StatusCode::FAILURE;
99 }
100 }
101 // if all goes well
102 return StatusCode::SUCCESS;
103}
104
106 // resolving lookuptable file
107
108 ATH_MSG_INFO("[PunchThroughG4Tool] PunchThroughG4Tool::initializePhysics() called");
109 std::string resolvedFileName = PathResolverFindCalibFile (m_filenameLookupTable);
110 if (resolvedFileName.empty()) {
111 ATH_MSG_ERROR( "[PunchThroughG4Tool] Parametrisation file '" << m_filenameLookupTable << "' not found" );
112 return StatusCode::FAILURE;
113 }
114 ATH_MSG_DEBUG( "[PunchThroughG4Tool] Parametrisation file found: " << resolvedFileName );
115 // open the LookupTable file
116 m_fileLookupTable = new TFile( resolvedFileName.c_str(), "READ");
117 if (!m_fileLookupTable) {
118 ATH_MSG_WARNING("[PunchThroughG4Tool] unable to open the lookup-table for the punch-through simulation (file does not exist)");
119 return StatusCode::FAILURE;
120 }
121
122 if (!m_fileLookupTable->IsOpen()) {
123 ATH_MSG_WARNING("[PunchThroughG4Tool] unable to open the lookup-table for the punch-through simulation (wrong or empty file?)");
124 return StatusCode::FAILURE;
125 }
126
127 //--------------------------------------------------------------------------------
128 // register all the punch-through particles which will be simulated
130
131 //--------------------------------------------------------------------------------
132 // register all the punch-through correlations between particles to be simulated
134
135 //--------------------------------------------------------------------------------
136 // close the file with the lookuptable
137 m_fileLookupTable->Close();
138
139 // Geometry identifier service
140 if ( !m_geoIDSvc.empty() && m_geoIDSvc.retrieve().isFailure())
141 {
142 ATH_MSG_FATAL ( "[PunchThroughG4Tool] Could not retrieve GeometryIdentifier Service. Abort");
143 return StatusCode::FAILURE;
144 }
145
146 //envelope definition service
147 if (m_envDefSvc.retrieve().isFailure() )
148 {
149 ATH_MSG_ERROR( "[PunchThroughG4Tool] Could not retrieve " << m_envDefSvc );
150 return StatusCode::FAILURE;
151 }
152
153 // direct-initialization of the Muon and Calo boundaries (giving R, Z coordinates)
154 // can also do like below (RZPairVector is vector<pair<double, double>>)
155 // const RZPairVector* rzMS = &(m_envDefSvc->getMuonRZBoundary());
156 const std::vector<std::pair<double, double>>* rzMS = &(m_envDefSvc->getMuonRZBoundary());
157 const std::vector<std::pair<double, double>>* rzCalo = &(m_envDefSvc->getCaloRZBoundary());
158 ATH_CHECK(checkCaloMSBoundaries(rzMS, rzCalo));
159 return StatusCode::SUCCESS;
160}
161
163 //--------------------------------------------------------------------------------
164 // TODO: implement punch-through parameters for different m_pdgInitiators
165 // currently m_pdgInitiators is only used to filter out particles,
166 // inside computePunchThroughParticles
167
168 // check if more correlations were given than particle types were registered
169 unsigned int numCorrelations = m_correlatedParticle.size();
170 if ( numCorrelations > m_punchThroughParticles.size() )
171 {
172 ATH_MSG_WARNING("[PunchThroughG4Tool] more punch-through particle correlations are given, than punch-through particle types are registered (skipping the last ones)");
173 numCorrelations = m_punchThroughParticles.size();
174 }
175
176 // now register correlation between particles
177 for ( unsigned int num = 0; num < numCorrelations; num++ )
178 {
179 const int pdg1 = m_punchThroughParticles[num];
180 const int pdg2 = m_correlatedParticle[num];
181 const double fullCorrEnergy = ( num < m_fullCorrEnergy.size() ) ? m_fullCorrEnergy[num] : 0.;
182 const double minCorrEnergy = ( num < m_minCorrEnergy.size() ) ? m_minCorrEnergy[num] : 0.;
183
184 // if correlatedParticle==0 is given -> no correlation
185 if ( ! pdg2) continue;
186 // register it
187 if ( registerCorrelation(pdg1, pdg2, minCorrEnergy, fullCorrEnergy) != StatusCode::SUCCESS )
188 {
189 ATH_MSG_ERROR("[PunchThroughG4Tool] unable to register punch-through particle correlation for pdg1=" << pdg1 << " pdg2=" << pdg2 );
190 return StatusCode::FAILURE;
191 }
192 }
193 // if all goes well
194 return StatusCode::SUCCESS;
195}
196
198 //m_R1, m_R2, m_z1, m_z2
199 std::vector<double> caloMSVarsVect = {m_R1, m_R2, m_z1, m_z2};
200 ATH_MSG_DEBUG("[PunchThroughG4Tool] getCaloMSVars()==> m_R1 = "<< caloMSVarsVect[0] << ", m_R2 = " << caloMSVarsVect[1] << ", m_z1 = " << caloMSVarsVect[2] << ", m_z2 = " << caloMSVarsVect[3]);
201
202 return caloMSVarsVect;
203}
204
205StatusCode PunchThroughG4Tool::checkCaloMSBoundaries(const std::vector<std::pair<double, double>>* rzMS,
206 const std::vector<std::pair<double, double>>* rzCalo)
207{
208 bool found1, found2;
209 found1=false; found2=false;
210
211 // procedure below is to find intersection of calo and MS boundary (right at the edge of calo and into MS)
212 // at least 2 distinct calo-MS boundary points must be found to proceed
213 for ( const auto & [r_tempCalo , z_tempCalo] : *rzCalo )
214 {
215 // calo point must be more than beampipe radius
216 if (r_tempCalo> m_beamPipe)
217 {
218 for ( const auto & [r_tempMS , z_tempMS] : *rzMS )
219 {
220 // first point, if R and Z of calo matches R and Z of MS
221 if (r_tempCalo==r_tempMS && z_tempCalo==z_tempMS && found1==false )
222 {
223 m_R1=r_tempMS;
224 m_z1=std::fabs(z_tempMS);
225 found1=true;
226 continue;
227 }
228 // second point, if R and Z of calo matches R and Z of MS
229 else if (r_tempCalo==r_tempMS && z_tempCalo==z_tempMS && r_tempCalo!=m_R1 && std::fabs(z_tempCalo)!=m_z1)
230 {
231 m_R2=r_tempMS;
232 m_z2=std::fabs(z_tempMS);
233 found2=true;
234 }
235 }
236
237 if (found1==true && found2==true) break;
238 }
239 }
240
241 //in case geometry description changes
242 if (found1 == false){
243 ATH_MSG_ERROR ("[PunchThroughG4Tool] first coordinate of calo-MS border not found");
244 return StatusCode::FAILURE;
245 }
246 if (found2 == false){
247 ATH_MSG_ERROR ("[PunchThroughG4Tool] second coordinate of calo-MS border not found; first one is: R1 ="<<m_R1<<" z1 ="<<m_z1);
248 return StatusCode::FAILURE;
249 }
250
251 //now order the found values
252 if (m_R1>m_R2) { std::swap(m_R1, m_R2); } //m_R1 - smaller one
253 if (m_z1<m_z2) { std::swap(m_z1, m_z2); } //m_z1 - bigger one
254
255 if (m_R1==m_R2 || m_z1==m_z2){
256 ATH_MSG_ERROR ("[PunchThroughG4Tool] Bug in propagation calculation! R1="<<m_R1<<" R2 = "<<m_R2<<" z1="<<m_z1<<" z2= "<<m_z2 );
257 return StatusCode::FAILURE;
258 }
259
260 ATH_MSG_DEBUG ("[PunchThroughG4Tool] calo-MS boundary coordinates: R1="<<m_R1<<" R2 = "<<m_R2<<" z1="<<m_z1<<" z2= "<<m_z2);
261
262 return StatusCode::SUCCESS;
263}
264
266{
267 ATH_MSG_DEBUG( "[PunchThroughG4Tool] finalize() starting" );
268 for(auto & each : m_particles) {
269 delete each.second;
270 }
271
272 ATH_MSG_DEBUG( "[PunchThroughG4Tool] finalize() successful" );
273
274 return StatusCode::SUCCESS;
275}
276
277void PunchThroughG4Tool::checkParticleTable(G4ParticleTable &ptable, int secondarySignedPDG){
278 // Get Geant4 particle definition
279 G4ParticleDefinition* secG4Particle = ptable.FindParticle(secondarySignedPDG);
280 double mass = secG4Particle->GetPDGMass();
281 ATH_MSG_DEBUG("[PunchThroughG4Tool::checkParticleTable] secondarySignedPDG = " << secondarySignedPDG << ", mass = "<< mass);
282}
283
284std::vector<std::map<std::string, double>> PunchThroughG4Tool::computePunchThroughParticles(const G4FastTrack& fastTrack, CLHEP::HepRandomEngine* rndmEngine, double punchThroughProbability, double punchThroughClassifierRand)
285{
286 ATH_MSG_DEBUG( "[PunchThroughG4Tool] starting punch-through simulation");
287
288 // holder to all secondary particle kinematics
289 std::vector<std::map<std::string, double>> secKinematicsMapVect;
290
291 // Get primary Geant4 track (main particle producing punchthrough)
292 const G4Track * g4PrimaryTrack = fastTrack.GetPrimaryTrack();
293
294 // Get Geant4 particle definition
295 const G4ParticleDefinition * mainG4Particle = g4PrimaryTrack->GetDefinition();
296
297 // Printing for debugging
298 ATH_MSG_DEBUG("PunchThroughG4Tool::computePunchThroughParticles Debug:"
299 << " Incoming particle:"
300 << " E=" << g4PrimaryTrack->GetKineticEnergy()/CLHEP::GeV << " GeV"
301 << " theta=" << g4PrimaryTrack->GetMomentumDirection().theta()
302 << " phi=" << g4PrimaryTrack->GetMomentumDirection().phi()
303 << " PDG=" << (mainG4Particle ? mainG4Particle->GetPDGEncoding() : -999)
304 << " Name=" << (mainG4Particle ? mainG4Particle->GetParticleName() : "UNKNOWN"));
305
306 // Get primary Geant4 particle properties
307 int pdgID = mainG4Particle->GetPDGEncoding();
308 float mainPartMass = mainG4Particle->GetPDGMass();
309 float mainMomMag2 = g4PrimaryTrack->GetMomentum().mag2();
310 float mainPartEta = g4PrimaryTrack->GetPosition().eta();
311 // get primary track's current position and momentum direction
312 const G4ThreeVector mainMomentumDir = g4PrimaryTrack->GetMomentumDirection();
313 const G4ThreeVector mainMomentum = g4PrimaryTrack->GetMomentum();
314 const G4ThreeVector mainPosition = g4PrimaryTrack->GetPosition();
315
316 // calculate incoming energy and eta
317 const double initEnergy = std::sqrt( mainMomMag2 + mainPartMass*mainPartMass );
318 const double initEta = mainPartEta;
319
320 ATH_MSG_DEBUG("[PunchThroughG4Tool] position of the input particle: r"<<mainPosition.perp()<<" z= "<<mainPosition.z() );
321
322 // check if it points to the calorimeter - if not, don't simulate
323 // first convert to Amg::Vector3D
324 const Amg::Vector3D amg3DPosVec(mainPosition.x(), mainPosition.y(), mainPosition.z());
325 const Amg::Vector3D amg3DMomDirVec(mainMomentumDir.x(), mainMomentumDir.y(), mainMomentumDir.z());
326 const Amg::Vector3D amg3DMomVec(mainMomentum.x(), mainMomentum.y(), mainMomentum.z());
327 // find nextGeoID to get AtlasDetDescr::AtlasRegion
328 AtlasDetDescr::AtlasRegion nextGeoID = m_geoIDSvc->identifyNextGeoID(amg3DPosVec,amg3DMomVec);
329 if ( nextGeoID != AtlasDetDescr::fAtlasCalo)
330 {
331 ATH_MSG_DEBUG("[PunchThroughG4Tool](GeoIDSvc) input particle doesn't point to calorimeter"<< "Next GeoID: "<< nextGeoID );
332 //return nullptr;
333 return {};
334 }
335
336 // check if the particle's pdg is registered as a punch-through-causing type
337 {
338 std::vector<int>::const_iterator pdgIt = m_pdgInitiators.begin();
339 std::vector<int>::const_iterator pdgItEnd = m_pdgInitiators.end();
340
341 std::vector<int>::const_iterator minEnergyIt = m_initiatorsMinEnergy.begin();
342 // loop over all known punch-through initiators
343 for ( ; pdgIt != pdgItEnd; ++pdgIt, ++minEnergyIt)
344 {
345 if (std::abs(pdgID) == *pdgIt){
346 if(initEnergy < *minEnergyIt){
347 ATH_MSG_DEBUG("[PunchThroughG4Tool] particle does not meet initiator min energy requirement. Dropping it in the calo.");
348 //return nullptr;
349 return {};
350 }
351 break;
352 }
353 }
354
355 // particle will not cause punch-through -> bail out
356 if (pdgIt == pdgItEnd)
357 {
358 ATH_MSG_DEBUG("[PunchThroughG4Tool] particle is not registered as punch-through initiator. Dropping it in the calo.");
359 //return nullptr;
360 return {};
361 }
362 }
363
364 if(initEta < m_initiatorsEtaRange.value().at(0) || initEta > m_initiatorsEtaRange.value().at(1) ){
365 ATH_MSG_DEBUG("[PunchThroughG4Tool] particle does not meet initiator eta range requirement. Dropping it in the calo.");
366 //return nullptr;
367 return {};
368 }
369
370 ATH_MSG_DEBUG("[PunchThroughG4Tool] punchThroughProbability output: " << punchThroughProbability << " RandFlat: " << punchThroughClassifierRand );
371
372 //Safety check: If probability < random number then don't simulate punch through
373 if( punchThroughClassifierRand > punchThroughProbability){
374 ATH_MSG_DEBUG("[PunchThroughG4Tool] particle not classified to create punch through. Dropping it in the calo.");
375 //return nullptr;
376 return {};
377 }
378
379 //if initial particle is on ID surface, points to the calorimeter, is a punch-through initiator, meets initiator min enery and eta range
380
381 // this is the place where the magic is done:
382 // test for each registered punch-through pdg if a punch-through
383 // occures and create these particles
384 // -> therefore loop over all registered pdg ids
385 // to keep track of the correlated particles which were already simulated:
386 // first int is pdg, second int is number of particles created
387
388 // interpolate energy and eta
389 const double interpEnergy = interpolateEnergy(initEnergy, rndmEngine);
390 const double interpEta = interpolateEta(initEta, rndmEngine);
391
392 std::map<int, int> corrPdgNumDone;
393
394 int maxTries = 10;
395 int nTries = 0;
396
397 // loop over all particle pdgs
398 ATH_MSG_DEBUG("[DEBUG] Enter computePunchThroughParticles: starting while (retry) loop");
399 while(secKinematicsMapVect.empty() && nTries < maxTries) { //ensure we always create at least one punch through particle, maxTries to catch very rare cases
400 // loop over all particle pdgs
401 ATH_MSG_DEBUG("[DEBUG] Retry " << nTries << ", secKinematicsMapVect.size=" << secKinematicsMapVect.size());
402 for (const auto& currentParticle : m_particles)
403 {
404 // the pdg that is currently treated
405 int doPdg = currentParticle.first;
406 // get the current particle's correlated pdg
407 int corrPdg = currentParticle.second->getCorrelatedPdg();
408
409 // if there is a correlated particle type to this one
410 if (corrPdg)
411 {
412 // find out if the current pdg was already simulated
413 std::map<int,int>::iterator itPdgPos = corrPdgNumDone.find(doPdg);
414 // if the current pdg was not simulated yet, find out if
415 // it's correlated one was simulated
416 if ( itPdgPos == corrPdgNumDone.end() ) itPdgPos = corrPdgNumDone.find(corrPdg);
417
418 // neither this nor the correlated particle type was simulated
419 // so far:
420 if ( itPdgPos == corrPdgNumDone.end() )
421 {
422 // -> roll a dice if we create this particle or its correlated one
423 if ( CLHEP::RandFlat::shoot(rndmEngine) > 0.5 ) doPdg = corrPdg;
424 // now create the particles with the given pdg and note how many
425 // particles of this pdg are created
426 corrPdgNumDone[doPdg] = getAllParticles(*g4PrimaryTrack, secKinematicsMapVect, rndmEngine, doPdg, interpEnergy, interpEta);
427 }
428
429 // one of the two correlated particle types was already simulated
430 // 'itPdgPos' points to the already simulated one
431 else
432 {
433 // get the pdg of the already simulated particle and the number
434 // of these particles that were created
435 const int donePdg = itPdgPos->first;
436 const int doneNumPart = itPdgPos->second;
437 // set the pdg of the particle type that will be done
438 if (donePdg == doPdg) doPdg = corrPdg;
439
440 // now create the correlated particles
441 getCorrelatedParticles(*g4PrimaryTrack, secKinematicsMapVect, doPdg, doneNumPart, rndmEngine, interpEnergy, interpEta);
442 // note: no need to take note, that this particle type is now simulated,
443 // since this is the second of two correlated particles, which is
444 // simulated and we do not have correlations of more than two particles.
445 }
446
447 // if no correlation for this particle
448 // -> directly create all particles with the current pdg
449 }
450 else{
451 getAllParticles(*g4PrimaryTrack, secKinematicsMapVect, rndmEngine, doPdg, interpEnergy, interpEta);
452 }
453 } // for-loop over all particle pdgs
454
455 // Debugging: Stored properties secondary particles to be simulated
456 if (msgLvl(MSG::DEBUG) && !secKinematicsMapVect.empty()) {
457 for (size_t i = 0; i < secKinematicsMapVect.size(); ++i) {
458 ATH_MSG_DEBUG("Secondary[" << i << "] kinematics:");
459 for (const auto& kv : secKinematicsMapVect[i]) {
460 ATH_MSG_DEBUG(" " << kv.first << " = " << kv.second);
461 }
462 }
463 }
464
465 // increment so that we can escape the while loop at maxTries
466 nTries++;
467 }
468
469 return secKinematicsMapVect;
470}
471
472int PunchThroughG4Tool::getAllParticles(const G4Track& g4PrimaryTrack, std::vector<std::map<std::string, double>> &secKinematicsMapVect, CLHEP::HepRandomEngine* rndmEngine, int pdg, double interpEnergy, double interpEta, int numParticles)
473{
474 // Get initial Geant4 position theta and phi
475 float initParticleTheta = g4PrimaryTrack.GetPosition().theta();
476 float initParticlePhi = g4PrimaryTrack.GetPosition().phi();
477
478 // get the current particle
480 double minAllowedEnergy = p->getMinEnergy();
481
482 // if no number of particles (=-1) was handed over as an argument
483 // -> get the number of particles from the pdf
484 if ( numParticles < 0 )
485 {
486 // prepare the function arguments for the PunchThroughPDFCreator class
487 std::vector<int> parameters;
488 parameters.push_back( std::round(interpEnergy) );
489 parameters.push_back( std::round(interpEta*100) );
490 // the maximum number of particles which should be produced
491 // if no maximum number is given, this is -1
492 int maxParticles = p->getMaxNumParticles();
493
494 // get the right number of punch-through particles
495 // and ensure that we do not create too many particles
496 do
497 {
498 numParticles = int( p->getNumParticlesPDF()->getRand(rndmEngine, parameters) );
499
500 // scale the number of particles if requested
501 numParticles = lround( numParticles *= p->getNumParticlesFactor() );
502 }
503 while ( (maxParticles >= 0.) && (numParticles > maxParticles) );
504 }
505
506 ATH_MSG_DEBUG("[PunchThroughG4Tool] adding " << numParticles << " punch-through particles with pdg code: " << pdg);
507
508 // get how many secondary particles to be created
509 for ( int numCreated = 0; numCreated < numParticles; numCreated++ )
510 {
511 // create one particle which fullfills the right energy distribution
512 std::map<std::string, double> secondaryKinematicsMap = getOneParticleKinematics(rndmEngine, pdg, initParticleTheta, initParticlePhi, interpEnergy, interpEta);
513
514 //safety to cater minimum energy as rest mass
515 double energy = secondaryKinematicsMap.at("energy");
516 if(energy > minAllowedEnergy){
517 // push to the vector of maps
518 secKinematicsMapVect.push_back(secondaryKinematicsMap);
519 }
520 }
521
522 // Get the size of the vector
523 std::size_t numSecondaries = secKinematicsMapVect.size();
524
525 // the actual (pre) number of particles which was created is numSecondaries, which is incremented in the loop
526 return (numSecondaries);
527}
528
529int PunchThroughG4Tool::getCorrelatedParticles(const G4Track& g4PrimaryTrack, std::vector<std::map<std::string, double>> &secKinematicsMapVect, int pdg, int corrParticles, CLHEP::HepRandomEngine* rndmEngine, double interpEnergy, double interpEta)
530{
531 // Get Geant4 particle definition
532 const G4ParticleDefinition * mainG4Particle = g4PrimaryTrack.GetDefinition();
533 // Get Geant4 particle pdgID
534 float mainMomMag2 = g4PrimaryTrack.GetMomentum().mag2();
535 float mainPartMass = mainG4Particle->GetPDGMass();
536
537 // get the PunchThroughParticle class
539
540 const double initEnergy = std::sqrt( mainMomMag2 + mainPartMass*mainPartMass );
541
542 // (1.) decide if we do correlation or not
543 double rand = CLHEP::RandFlat::shoot(rndmEngine)
544 *(p->getFullCorrelationEnergy() - p->getMinCorrelationEnergy())
545 + p->getMinCorrelationEnergy();
546
547 // if initEnergy less than random corr energy (meaning threshold for min energy for doing correlation not passed)
548 if ( initEnergy < rand )
549 {
550 // here we do not do correlation
551 return getAllParticles(g4PrimaryTrack, secKinematicsMapVect, rndmEngine, pdg, interpEnergy, interpEta);
552 }
553
554 // (2.) if this point is reached, we do correlation
555 // decide which 2d correlation histogram to use
556 double *histDomains = p->getCorrelationHistDomains();
557 TH2F *hist2d = nullptr;
558 // compute the center values of the lowE and highE
559 // correlation histogram domains
560 if ( initEnergy < histDomains[1])
561 {
562 // initial energy lower than border between lowEnergy and highEnergy histogram domain
563 // --> choose lowEnergy correlation histogram
564 hist2d = p->getCorrelationLowEHist();
565 }
566 else
567 {
568 double rand = CLHEP::RandFlat::shoot(rndmEngine)*(histDomains[2]-histDomains[1]) + histDomains[1];
569 hist2d = ( initEnergy < rand) ? p->getCorrelationLowEHist() : p->getCorrelationHighEHist();
570 }
571
572 // get the correlation 2d histogram
573 // now find out where on the x-axis the the bin for number of
574 // correlated particles is
575 Int_t xbin = hist2d->GetXaxis()->FindFixBin(corrParticles);
576 int numParticles = 0;
577 int maxParticles = p->getMaxNumParticles();
578 // now the distribution along the y-axis is a PDF for the number
579 // of 'pdg' particles
580 do
581 {
582 double rand = CLHEP::RandFlat::shoot(rndmEngine);
583 double sum = 0.;
584 for ( int ybin = 1; ybin <= hist2d->GetNbinsY(); ybin++ )
585 {
586 sum += hist2d->GetBinContent(xbin, ybin);
587 // check if we choose the current bin or not
588 if ( sum >= rand )
589 {
590 numParticles = ybin - 1;
591 break;
592 }
593 }
594 // scale the number of particles is requested
595 numParticles = lround( numParticles * p->getNumParticlesFactor() );
596 }
597 while ( (maxParticles >= 0.) && (numParticles > maxParticles) );
598
599 // finally create this exact number of particles
600 return getAllParticles(g4PrimaryTrack, secKinematicsMapVect, rndmEngine, pdg, interpEnergy, interpEta, numParticles);
601}
602
603std::map<std::string, double> PunchThroughG4Tool::getOneParticleKinematics(CLHEP::HepRandomEngine* rndmEngine, int secondaryPDG, float initParticleTheta, float initParticlePhi, double interpEnergy, double interpEta) const
604{
605 // get a local copy of the needed punch-through particle class
606 PunchThroughParticle *p = m_particles.at(secondaryPDG);
607
608 // (0.) get the pca / cdf group based on pdgId and eta, eta times 100, e.g eta -4 to 4 is from eta -400 to 400
609 int pcaCdfIterator = passedParamIterator(secondaryPDG, interpEta*100, m_xml_info_pca); //pca and cdf info should be of same size
610
611 ATH_MSG_DEBUG("[PunchThroughG4Tool] passedPCAIterator ==> pcaCdfIterator = "<< pcaCdfIterator <<" , pdg = "<< secondaryPDG <<" , interpEnergy = "<< interpEnergy <<" MeV, interpEta(*100) = "<< interpEta*100);
612
613 // (1.) decide if we create a particle or an anti-particle,
614 // this is from m_doAntiParticles properties
615 int anti = 1;
616 if ( p->getdoAnti() )
617 {
618 // get a random-value
619 double rand = CLHEP::RandFlat::shoot(rndmEngine);
620 // 50/50 chance to be a particle or its anti-particle
621 if (rand > 0.5) anti = -1;
622 }
623
624 // (2.) get the right punch-through distributions
625 // prepare the function arguments for the PunchThroughPDFCreator class
626 std::vector<int> parInitEnergyEta;
627 parInitEnergyEta.push_back( std::round(interpEnergy) );
628 parInitEnergyEta.push_back( std::round(interpEta*100) );
629
630 //initialise variables to store punch through particle kinematics
631 double energy = 0.;
632 double deltaTheta = 0.;
633 double deltaPhi = 0.;
634 double momDeltaTheta = 0.;
635 double momDeltaPhi = 0.;
636
637 double principal_component_0 = 0.;
638 double principal_component_1 = 0.;
639 double principal_component_2 = 0.;
640 double principal_component_3 = 0.;
641 double principal_component_4 = 0.;
642 std::vector<double> transformed_variables;
643
644 principal_component_0 = p->getPCA0PDF()->getRand(rndmEngine, parInitEnergyEta);
645 principal_component_1 = p->getPCA1PDF()->getRand(rndmEngine, parInitEnergyEta);
646 principal_component_2 = p->getPCA2PDF()->getRand(rndmEngine, parInitEnergyEta);
647 principal_component_3 = p->getPCA3PDF()->getRand(rndmEngine, parInitEnergyEta);
648 principal_component_4 = p->getPCA4PDF()->getRand(rndmEngine, parInitEnergyEta);
649
650 ATH_MSG_DEBUG("[PunchThroughG4Tool] Drawn punch through kinematics PCA components: PCA0 = "<< principal_component_0 <<" PCA1 = "<< principal_component_1 <<" PCA2 = "<< principal_component_2 <<" PCA3 = "<< principal_component_3 <<" PCA4 = "<< principal_component_4 );
651
652 std::vector<double> principal_components {
653 principal_component_0,
654 principal_component_1,
655 principal_component_2,
656 principal_component_3,
657 principal_component_4
658 };
659
660 transformed_variables = inversePCA(pcaCdfIterator,principal_components);
661
662 energy = inverseCdfTransform(transformed_variables.at(0), m_variable0_inverse_cdf[pcaCdfIterator]);
663 deltaTheta = inverseCdfTransform(transformed_variables.at(1), m_variable1_inverse_cdf[pcaCdfIterator]);
664 deltaPhi = inverseCdfTransform(transformed_variables.at(2), m_variable2_inverse_cdf[pcaCdfIterator]);
665 momDeltaTheta = inverseCdfTransform(transformed_variables.at(3), m_variable3_inverse_cdf[pcaCdfIterator]);
666 momDeltaPhi = inverseCdfTransform(transformed_variables.at(4), m_variable4_inverse_cdf[pcaCdfIterator]);
667
668 ATH_MSG_DEBUG("[PunchThroughG4Tool] Transformed punch through kinematics: energy = "<< energy <<" MeV deltaTheta = "<< deltaTheta <<" deltaPhi = "<< deltaPhi <<" momDeltaTheta = "<< momDeltaTheta <<" momDeltaPhi = "<< momDeltaPhi );
669
670 energy *= p->getEnergyFactor(); // scale the energy if requested
671
672 // safety: if energy less than minimum energy, add a small energy (10MeV to it to continue)
673 if (energy < p->getMinEnergy()) {
674 energy = p->getMinEnergy() + 10;
675 }
676
677 // (2.2) get the particles DeltaTheta relative to the incoming particle
678 double theta = 0;
679 // loop to keep theta within range [0,PI]
680 do
681 {
682 // decide if delta positive/negative
683 deltaTheta *= ( CLHEP::RandFlat::shoot(rndmEngine) > 0.5 ) ? 1. : -1.;
684
685 // calculate the exact theta value of the later created
686 // punch-through particle
687 theta = initParticleTheta + deltaTheta*p->getPosAngleFactor();
688 }
689 while ( (theta > M_PI) || (theta < 0.) );
690
691 // (2.3) get the particle's delta phi relative to the incoming particle
692 deltaPhi *= ( CLHEP::RandFlat::shoot(rndmEngine) > 0.5 ) ? 1. : -1.;
693
694 // keep phi within range [-PI,PI]
695 double phi = initParticlePhi + deltaPhi*p->getPosAngleFactor();
696 while ( std::fabs(phi) > 2*M_PI) phi /= 2.;
697 while (phi > M_PI) phi -= 2*M_PI;
698 while (phi < -M_PI) phi += 2*M_PI;
699
700 // (2.4) get the particle momentum delta theta, relative to its position
701 //
702 // loop to keep momTheta within range [0,PI]
703 double momTheta = 0.;
704 do
705 {
706 // decide if delta positive/negative
707 momDeltaTheta *= ( CLHEP::RandFlat::shoot(rndmEngine) > 0.5 ) ? 1. : -1.;
708 // calculate the exact momentum theta value of the later created
709 // punch-through particle
710 momTheta = theta + momDeltaTheta*p->getMomAngleFactor();
711 }
712 while ( (momTheta > M_PI) || (momTheta < 0.) );
713
714 // (2.5) get the particle momentum delta phi, relative to its position
715 momDeltaPhi *= ( CLHEP::RandFlat::shoot(rndmEngine) > 0.5 ) ? 1. : -1.;
716
717 double momPhi = phi + momDeltaPhi*p->getMomAngleFactor();
718 // keep momPhi within range [-PI,PI]
719 while ( std::fabs(momPhi) > 2*M_PI) momPhi /= 2.;
720 while (momPhi > M_PI) momPhi -= 2*M_PI;
721 while (momPhi < -M_PI) momPhi += 2*M_PI;
722
723 // store the kinematics in a map and return it
724 std::map<std::string, double> secondaryKinematicsMap;
725 secondaryKinematicsMap.insert({ "anti" , anti });
726 secondaryKinematicsMap.insert({ "secondaryPDG" , secondaryPDG });
727 secondaryKinematicsMap.insert({ "energy" , energy });
728 secondaryKinematicsMap.insert({ "theta" , theta });
729 secondaryKinematicsMap.insert({ "phi" , phi });
730 secondaryKinematicsMap.insert({ "momTheta" , momTheta });
731 secondaryKinematicsMap.insert({ "momPhi" , momPhi });
732
733 return secondaryKinematicsMap;
734}
735
736std::vector<std::map<std::string, double>> PunchThroughG4Tool::checkEnergySumFromSecondaries(double mainEnergyInit, std::vector<std::map<std::string, double>> &secKinematicsMapVect){
737 //initialize variables
738 double energy;
739 double totEnergySecondaries = 0;
740
741 // Check energy conservation
742 // Step 1: sum all of the energies from the vector of secondaries (map) together, to initialize procedure later
743 for (std::size_t i = 0; i < secKinematicsMapVect.size(); i++) {
744 energy = secKinematicsMapVect[i].at("energy");
745 totEnergySecondaries += energy;
746 }
747
748 // Step 2: Sort the vector based on "energy" in descending order using lambda expression
749 std::sort(
750 secKinematicsMapVect.begin(),
751 secKinematicsMapVect.end(),
752 [](const std::map<std::string, double>& lhs, const std::map<std::string, double>& rhs) {
753 return lhs.at("energy") > rhs.at("energy");
754 }
755 );
756
757 // Just for printing here
758 if(totEnergySecondaries > mainEnergyInit){
759 ATH_MSG_DEBUG("[PunchThroughG4Tool::checkEnergySumFromSecondaries] Case where energy of created secondaries more than parent track identified! ");
760 ATH_MSG_DEBUG("[PunchThroughG4Tool::checkEnergySumFromSecondaries] ==> TotalSecondariesEnergy = " << totEnergySecondaries << ", ParentEnergy = "<< mainEnergyInit);
761 }
762
763 // Step 3: Adjust the energy and remove secondary with lowest energy to ensure total energy of secondaries <= mainEnergyInit
764 while (totEnergySecondaries > mainEnergyInit && !secKinematicsMapVect.empty()) {
765 // Get the last map in the vector (which has the lowest energy due to sorting)
766 std::map<std::string, double> lastMap = secKinematicsMapVect.back();
767
768 // Get the energy value of the last map
769 double lastEnergy = lastMap.at("energy");
770
771 // Remove the last map from the vector
772 secKinematicsMapVect.pop_back();
773
774 // Subtract the removed energy from totEnergySecondaries
775 totEnergySecondaries -= lastEnergy;
776 }
777
778 //finally return the secKinematicsMapVect after the checks
779 return secKinematicsMapVect;
780}
781
782void PunchThroughG4Tool::createAllSecondaryTracks(G4ParticleTable &ptable, G4FastStep& fastStep, const G4Track& g4PrimaryTrack, std::vector<std::map<std::string, double>> &secKinematicsMapVect, G4TrackVector& secTrackCont, const std::vector<double> &caloMSVars){
783 // Get Geant4 particle definition
784 const G4ParticleDefinition* mainG4Particle = g4PrimaryTrack.GetDefinition();
785 float mainMomMag2 = g4PrimaryTrack.GetMomentum().mag2();
786 float mainPartMass = mainG4Particle->GetPDGMass();
787 double mainEnergyInit = std::sqrt(mainMomMag2 + mainPartMass * mainPartMass);
788
789 // Energy conservation: call checkEnergySumFromSecondaries to check/update secKinematicsMapVect
790 secKinematicsMapVect = checkEnergySumFromSecondaries(mainEnergyInit, secKinematicsMapVect);
791
792 // set number of secondary tracks to be produced
793 int numSecondaries = secKinematicsMapVect.size();
794
795 if(numSecondaries>0){ //safety
796 // set number of secondary tracks from numSecondaries above
797 fastStep.SetNumberOfSecondaryTracks(numSecondaries);
798
799 // Get current (global) time from original main G4Track
800 double currentTime = g4PrimaryTrack.GetGlobalTime();
801
802 // create the secondaries given the number of secondaries to be created
803 for(int i=0;i < numSecondaries; i++){
804 int anti = (int)(secKinematicsMapVect[i].at("anti"));
805 int secondaryPDG = (int)(secKinematicsMapVect[i].at("secondaryPDG"));
806 int signedPDG = secondaryPDG*anti;
807 double energy = secKinematicsMapVect[i].at("energy");
808 double theta = secKinematicsMapVect[i].at("theta");
809 double phi = secKinematicsMapVect[i].at("phi");
810 double momTheta = secKinematicsMapVect[i].at("momTheta");
811 double momPhi = secKinematicsMapVect[i].at("momPhi");
812
813 // (**) finally create the punch-through particle as a G4Track (secondary particle track)
814 ATH_MSG_DEBUG("[PunchThroughG4Tool::createAllSecondaryTracks] createSecondaryTrack input parameters: currentTime = " << currentTime << " anti? = "<< anti <<" signedPDG = "<< signedPDG <<" energy = "<< energy <<" theta = "<< theta <<" phi = "<< phi <<" momTheta = "<< momTheta << " momPhi " << momPhi);
815 G4Track* newSecTrack = createSecondaryTrack( ptable, fastStep, currentTime, signedPDG, energy, theta, phi, momTheta, momPhi, caloMSVars);
816
817 // if something went wrong and the secondary track is simply not created
818 if (!newSecTrack)
819 {
820 G4Exception("[PunchThroughG4Tool::createAllSecondaryTracks]", "ExceptionError", FatalException, "something went wrong while creating punch-through particle tracks");
821 return;
822 }
823
824 // add this secondary track to the g4trackvector
825 secTrackCont.push_back( newSecTrack );
826 }
827 }
828
829 //printout: fastStep.DumpInfo()
830}
831
832G4Track* PunchThroughG4Tool::createSecondaryTrack( G4ParticleTable &ptable, G4FastStep& fastStep, double currentTime, int secondarySignedPDG,
833 double energy, double theta, double phi,double momTheta, double momPhi, const std::vector<double> &caloMSVars)
834{
835 //initialize to nullptr
836 G4Track *newSecTrack = nullptr;
837
838 G4ParticleDefinition* secG4Particle = ptable.FindParticle(secondarySignedPDG);
839 double mass = secG4Particle->GetPDGMass();
840
841 // the intersection point with Calo-MS surface
842 G4ThreeVector posVec = punchTroughPosPropagator(theta, phi, caloMSVars[0], caloMSVars[1], caloMSVars[2], caloMSVars[3]);
843
844 // set up the real punch-through particle at this position
845 // set up the momentum vector of this particle as a GlobalMomentum
846 // by using the given energy and mass of the particle and also using
847 // the given theta and phi
848 G4ThreeVector momVec;
849 double momMag = std::sqrt(energy*energy - mass*mass);
850 momVec.setRThetaPhi(momMag,momTheta,momPhi);
851 ATH_MSG_DEBUG("[PunchThroughG4Tool]::createSecondaryTrack] setRThetaPhi pre input parameters: energy = " << energy <<" mass = "<< mass);
852 ATH_MSG_DEBUG("[PunchThroughG4Tool]::createSecondaryTrack] setRThetaPhi input parameters: std::sqrt(energy*energy - mass*mass) = " << std::sqrt(energy*energy - mass*mass) << " momTheta = "<< momTheta <<" momPhi = "<< momPhi);
853
854 // create dynamic particle
855 G4DynamicParticle dynParticle(secG4Particle,momVec);
856 newSecTrack = fastStep.CreateSecondaryTrack(dynParticle, posVec, currentTime, false); // use position in global coordinates instead
857
858 return newSecTrack;
859}
860
862 return 0.5 * TMath::Erfc(-x * M_SQRT1_2);
863}
864
865std::vector<double> PunchThroughG4Tool::dotProduct(const std::vector<std::vector<double>> &m, const std::vector<double> &v)
866{
867 std::vector<double> result;
868 result.reserve(m.size());
869 for (const auto& r : m){
870 result.push_back(std::inner_product(v.begin(), v.end(), r.begin(), 0.0));
871 }
872
873 return result;
874}
875
876template<typename T>
877std::vector<T> str_to_list(const std::string_view str)
878{
879 constexpr char delimiters = ',';
880 std::vector<T> tokens;
881 // Skip delimiters at beginning.
882 std::string_view::size_type lastPos = str.find_first_not_of(delimiters, 0);
883 // Find first "non-delimiter".
884 std::string_view::size_type pos = str.find_first_of(delimiters, lastPos);
885
886 while (std::string_view::npos != pos || std::string_view::npos != lastPos) {
887 // Found a token, add it to the vector.
888 std::string_view numbStr = str.substr(lastPos, pos - lastPos);
889 T num = -9999;
890 std::from_chars(numbStr.data(), numbStr.data() + numbStr.size(), num);
891 tokens.push_back(num);
892 // Skip delimiters. Note the "not_of"
893 lastPos = str.find_first_not_of(delimiters, pos);
894 // Find next "non-delimiter"
895 pos = str.find_first_of(delimiters, lastPos);
896 }
897 return tokens;
898}
899
900int PunchThroughG4Tool::passedParamIterator(int pid, double eta, const std::vector<std::map<std::string, std::string>> &mapvect) const
901{
902 //convert the pid to absolute value and string for query
903 int pidStrSingle = std::abs(pid);
904 //STEP 1
905 //filter items matching pid first
906
907 for (unsigned int i = 0; i < mapvect.size(); i++){
908 const std::string &pidStr = mapvect[i].at("pidStr");
909 auto v = str_to_list<int>(pidStr);
910 if(std::find(v.begin(), v.end(),pidStrSingle)==v.end()) continue;
911 const std::string &etaMinsStr = mapvect[i].at("etaMins");
912 const std::string &etaMaxsStr = mapvect[i].at("etaMaxs");
913 std::vector<double> etaMinsVect = str_to_list<double>(etaMinsStr);
914 std::vector<double> etaMaxsVect = str_to_list<double>(etaMaxsStr);
915 assert(etaMaxsVect.size() == etaMinsVect.size());
916 for (unsigned int j = 0; j < etaMinsVect.size(); j++){ // assume size etaMinsVect == etaMaxsVect
917 double etaMinToCompare = etaMinsVect[j];
918 double etaMaxToCompare = etaMaxsVect[j];
919 if((eta >= etaMinToCompare) && (eta < etaMaxToCompare)){
920 //PASS CONDITION
921 //then choose the passing one and note it's iterator
922 return (i); //in case more than 1 match (ambiguous case)
923 }
924 }
925 }
926 return 0;
927}
928
929std::vector<std::map<std::string, std::string>> PunchThroughG4Tool::getInfoMap(const std::string& mainNode, const std::string &xmlFilePath){
930 // Initialize pointers
931 xmlDocPtr doc;
932 xmlChar* xmlBuff = nullptr;
933
934 std::vector<std::map<std::string, std::string>> xml_info;
935 doc = xmlParseFile( xmlFilePath.c_str() );
936
937 //check info first
938 for( xmlNodePtr nodeRoot = doc->children; nodeRoot != nullptr; nodeRoot = nodeRoot->next) {
939 if (xmlStrEqual( nodeRoot->name, BAD_CAST mainNode.c_str() )) {
940 for( xmlNodePtr nodeRootChild = nodeRoot->children; nodeRootChild != nullptr; nodeRootChild = nodeRootChild->next ) {
941 if (xmlStrEqual( nodeRootChild->name, BAD_CAST "info" )) {
942 if (nodeRootChild->children != NULL) {
943 for( xmlNodePtr infoNode = nodeRootChild->children; infoNode != nullptr; infoNode = infoNode->next) {
944 if(xmlStrEqual( infoNode->name, BAD_CAST "item" )){
945 std::map<std::string, std::string> xml_info_item;
946
947 if ((xmlBuff = xmlGetProp(infoNode, BAD_CAST "name")) != nullptr) {
948 xml_info_item.insert({"name", reinterpret_cast<const char*>(xmlBuff)});
949 }
950 if ((xmlBuff = xmlGetProp(infoNode, BAD_CAST "etaMins")) != nullptr) {
951 xml_info_item.insert({"etaMins", reinterpret_cast<const char*>(xmlBuff)});
952 }
953 if ((xmlBuff = xmlGetProp(infoNode, BAD_CAST "etaMaxs")) != nullptr) {
954 xml_info_item.insert({"etaMaxs", reinterpret_cast<const char*>(xmlBuff)});
955 }
956 if ((xmlBuff = xmlGetProp(infoNode, BAD_CAST "pidStr")) != nullptr) {
957 xml_info_item.insert({"pidStr", reinterpret_cast<const char*>(xmlBuff)});
958 }
959
960 xml_info.push_back(xml_info_item);
961 }
962 }
963 }
964 }
965 }
966 }
967 }
968
969 // free memory when done
970 xmlFreeDoc(doc);
971
972 return xml_info;
973}
974
975std::vector<double> PunchThroughG4Tool::inversePCA(int pcaCdfIterator, std::vector<double> &variables) const
976{
977 std::vector<double> transformed_variables = dotProduct(m_inverse_PCA_matrix[pcaCdfIterator], variables);
978
979 std::transform (transformed_variables.begin(), transformed_variables.end(), m_PCA_means[pcaCdfIterator].begin(), transformed_variables.begin(), std::plus<double>()); // + means
980
981 return transformed_variables;
982}
983
984StatusCode PunchThroughG4Tool::initializeInversePCA(const std::string & inversePCAConfigFile){
985 // Initialize pointers
986 xmlDocPtr doc;
987 xmlChar* xmlBuff = nullptr;
988
989 doc = xmlParseFile( inversePCAConfigFile.c_str() );
990
991 ATH_MSG_DEBUG( "[PunchThroughG4Tool] Loading inversePCA: " << inversePCAConfigFile);
992
993 //check info first
994 m_xml_info_pca = getInfoMap("PCAinverse",inversePCAConfigFile);
995
996 //do the saving
997 for (unsigned int i = 0; i < m_xml_info_pca.size(); i++) {
998 std::vector<std::vector<double>> PCA_matrix;
999 ATH_MSG_DEBUG( "[PunchThroughG4Tool] m_xml_info_pca[" << i << "].at('name') = " << m_xml_info_pca[i].at("name"));
1000
1001 for( xmlNodePtr nodeRoot = doc->children; nodeRoot != nullptr; nodeRoot = nodeRoot->next) {
1002 if (xmlStrEqual( nodeRoot->name, BAD_CAST "PCAinverse" )) {
1003 for( xmlNodePtr nodePCAinverse = nodeRoot->children; nodePCAinverse != nullptr; nodePCAinverse = nodePCAinverse->next ) {
1004
1005 if (xmlStrEqual( nodePCAinverse->name, BAD_CAST m_xml_info_pca[i].at("name").c_str() )) {
1006 if (nodePCAinverse->children != NULL) {
1007 for( xmlNodePtr pcaNode = nodePCAinverse->children; pcaNode != nullptr; pcaNode = pcaNode->next) {
1008
1009 if (xmlStrEqual( pcaNode->name, BAD_CAST "PCAmatrix" )) {
1010 std::vector<double> PCA_matrix_row;
1011 for (int i = 0; i <= 4; ++i) {
1012 std::string propName = "comp_" + std::to_string(i); // Dynamically create property name
1013 if ((xmlBuff = xmlGetProp(pcaNode, BAD_CAST propName.c_str())) != nullptr) {
1014 PCA_matrix_row.push_back(atof(reinterpret_cast<const char*>(xmlBuff))); // Convert and push to the row
1015 }
1016 }
1017 PCA_matrix.push_back(PCA_matrix_row);
1018 }
1019 else if (xmlStrEqual( pcaNode->name, BAD_CAST "PCAmeans" )) {
1020 std::vector<double> PCA_means_row;
1021 for (int i = 0; i <= 4; ++i) {
1022 std::string propName = "mean_" + std::to_string(i); // Dynamically create property name
1023 if ((xmlBuff = xmlGetProp(pcaNode, BAD_CAST propName.c_str())) != nullptr) {
1024 PCA_means_row.push_back(atof(reinterpret_cast<const char*>(xmlBuff))); // Convert and push to the row
1025 }
1026 }
1027 m_PCA_means.push_back(PCA_means_row);
1028 }
1029
1030 }
1031
1032 }
1033 }
1034
1035 }
1036 }
1037 }
1038 m_inverse_PCA_matrix.push_back(PCA_matrix);
1039 }
1040
1041 // free memory when done
1042 xmlFreeDoc(doc);
1043
1044 return StatusCode::SUCCESS;
1045}
1046
1047StatusCode PunchThroughG4Tool::initializeInverseCDF(const std::string & inverseCdfConfigFile){
1048 std::map<double, double> variable0_inverse_cdf_row;
1049 std::map<double, double> variable1_inverse_cdf_row;
1050 std::map<double, double> variable2_inverse_cdf_row;
1051 std::map<double, double> variable3_inverse_cdf_row;
1052 std::map<double, double> variable4_inverse_cdf_row;
1053
1054 // Initialize pointers
1055 xmlDocPtr doc;
1056
1057 //parse xml that contains config for inverse CDF for each of punch through particle kinematics
1058
1059 doc = xmlParseFile( inverseCdfConfigFile.c_str() );
1060
1061 ATH_MSG_DEBUG( "[PunchThroughG4Tool] Loading inverse CDF: " << inverseCdfConfigFile);
1062
1063 //check info first
1064 m_xml_info_cdf = getInfoMap("CDFMappings",inverseCdfConfigFile);
1065
1066 //do the saving
1067 for (unsigned int i = 0; i < m_xml_info_cdf.size(); i++) {
1068 ATH_MSG_DEBUG( "[PunchThroughG4Tool] m_xml_info_cdf[" << i << "].at('name') = " << m_xml_info_cdf[i].at("name"));
1069
1070 for( xmlNodePtr nodeRoot = doc->children; nodeRoot != nullptr; nodeRoot = nodeRoot->next) {
1071 if (xmlStrEqual( nodeRoot->name, BAD_CAST "CDFMappings" )) {
1072 for( xmlNodePtr typeMappings = nodeRoot->children; typeMappings != nullptr; typeMappings = typeMappings->next ) {
1073 if (xmlStrEqual( typeMappings->name, BAD_CAST m_xml_info_cdf[i].at("name").c_str() )) {
1074 if (typeMappings->children != NULL) {
1075 for( xmlNodePtr nodeMappings = typeMappings->children; nodeMappings != nullptr; nodeMappings = nodeMappings->next) {
1076
1077 if (xmlStrEqual( nodeMappings->name, BAD_CAST "variable0" )) {
1078 variable0_inverse_cdf_row = getVariableCDFmappings(nodeMappings);
1079 }
1080 else if (xmlStrEqual( nodeMappings->name, BAD_CAST "variable1" )) {
1081 variable1_inverse_cdf_row = getVariableCDFmappings(nodeMappings);
1082 }
1083 else if (xmlStrEqual( nodeMappings->name, BAD_CAST "variable2" )) {
1084 variable2_inverse_cdf_row = getVariableCDFmappings(nodeMappings);
1085 }
1086 else if (xmlStrEqual( nodeMappings->name, BAD_CAST "variable3" )) {
1087 variable3_inverse_cdf_row = getVariableCDFmappings(nodeMappings);
1088 }
1089 else if (xmlStrEqual( nodeMappings->name, BAD_CAST "variable4" )) {
1090 variable4_inverse_cdf_row = getVariableCDFmappings(nodeMappings);
1091 }
1092 }
1093
1094 }
1095 }
1096 }
1097 }
1098 }
1099 m_variable0_inverse_cdf.push_back(variable0_inverse_cdf_row);
1100 m_variable1_inverse_cdf.push_back(variable1_inverse_cdf_row);
1101 m_variable2_inverse_cdf.push_back(variable2_inverse_cdf_row);
1102 m_variable3_inverse_cdf.push_back(variable3_inverse_cdf_row);
1103 m_variable4_inverse_cdf.push_back(variable4_inverse_cdf_row);
1104 }
1105
1106 // free memory when done
1107 xmlFreeDoc(doc);
1108
1109 return StatusCode::SUCCESS;
1110}
1111
1112std::map<double, double> PunchThroughG4Tool::getVariableCDFmappings(xmlNodePtr& nodeParent){
1113 std::map<double, double> mappings;
1114 xmlChar* xmlBuff = nullptr;
1115 double ref = -1;
1116 double quant = -1;
1117 for( xmlNodePtr node = nodeParent->children; node != nullptr; node = node->next ) {
1118 //Get min and max values that we normalise values to
1119 if (xmlStrEqual( node->name, BAD_CAST "CDFmap" )) {
1120 if ((xmlBuff = xmlGetProp(node, BAD_CAST "ref")) != nullptr) {
1121 ref = atof( reinterpret_cast<const char*> (xmlBuff) );
1122 }
1123 if ((xmlBuff = xmlGetProp(node, BAD_CAST "quant")) != nullptr) {
1124 quant = atof( reinterpret_cast<const char*> (xmlBuff) );
1125 }
1126 mappings.insert(std::pair<double, double>(ref, quant) );
1127 }
1128 }
1129
1130 return mappings;
1131}
1132
1133double PunchThroughG4Tool::inverseCdfTransform(double variable, const std::map<double, double> &inverse_cdf_map) {
1134
1135 double norm_cdf = normal_cdf(variable);
1136
1137 auto upper = inverse_cdf_map.upper_bound(norm_cdf);
1138 auto lower = upper--;
1139
1140 double m = (upper->second - lower->second)/(upper->first - lower->first);
1141 double c = lower->second - m * lower->first;
1142 double transformed = m * norm_cdf + c;
1143
1144 return transformed;
1145
1146}
1147
1148double PunchThroughG4Tool::interpolateEnergy(const double &energy, CLHEP::HepRandomEngine* rndmEngine) const{
1149
1150 ATH_MSG_DEBUG("[PunchThroughG4Tool] interpolating incoming energy: " << energy);
1151
1152 std::string energyPointsString;
1153 for (auto element:m_energyPoints){
1154 energyPointsString += std::to_string(element) + " ";
1155 }
1156
1157 ATH_MSG_DEBUG("[PunchThroughG4Tool] available energy points: " << energyPointsString);
1158
1159 auto const upperEnergy = std::upper_bound(m_energyPoints.begin(), m_energyPoints.end(), energy);
1160
1161 if(upperEnergy == m_etaPoints.end()){ //if no energy greater than input energy, choose greatest energy
1162 ATH_MSG_DEBUG("[PunchThroughG4Tool] incoming energy > largest energy point, returning greatest energy point: " << m_energyPoints.back());
1163 return m_energyPoints.back();
1164 }
1165 else if(upperEnergy == m_etaPoints.begin()){ //if smallest energy greater than input energy, choose smallest energy
1166 ATH_MSG_DEBUG("[PunchThroughG4Tool] incoming energy < smallest energy point, returning smallest energy point: " << *upperEnergy);
1167 return *upperEnergy;
1168 }
1169
1170 ATH_MSG_DEBUG("[PunchThroughG4Tool] energy points upper_bound: "<< *upperEnergy);
1171
1172 double randomShoot = CLHEP::RandFlat::shoot(rndmEngine);
1173
1174 ATH_MSG_DEBUG("[PunchThroughG4Tool] Shooting random number: "<< randomShoot);
1175
1176 double midPoint = *std::prev(upperEnergy)*M_SQRT2;
1177
1178 if(energy < midPoint){ //if energy smaller than mid point in log(energy)
1179
1180 double distance = std::abs(energy - *std::prev(upperEnergy))/((midPoint) - *std::prev(upperEnergy));
1181
1182 ATH_MSG_DEBUG( "[PunchThroughG4Tool] incoming energy is closest to prev(upper_bound) in log(energy), distance: " << distance );
1183
1184 if(randomShoot < distance){
1185 ATH_MSG_DEBUG( "[PunchThroughG4Tool] randomShoot < distance, returning upper_bound " << *upperEnergy );
1186 return *upperEnergy;
1187 }
1188 ATH_MSG_DEBUG( "[PunchThroughG4Tool] randomShoot > distance, returning prev(upper_bound) " << *std::prev(upperEnergy) );
1189
1190 return *std::prev(upperEnergy);
1191 }
1192 else if(energy > midPoint){ //if energy greater than mid point in log(energy)
1193
1194 double distance = std::abs(energy - *upperEnergy)/((*upperEnergy - midPoint));
1195
1196 ATH_MSG_DEBUG( "[PunchThroughG4Tool] incoming energy is closest to upper_bound in log(energy), distance: " << distance );
1197
1198 if(randomShoot < distance){
1199 ATH_MSG_DEBUG( "[PunchThroughG4Tool] randomShoot < distance, returning prev(upper_bound) " << *std::prev(upperEnergy) );
1200 return *std::prev(upperEnergy);
1201 }
1202 ATH_MSG_DEBUG( "[PunchThroughG4Tool] randomShoot > distance, returning upper_bound " << *upperEnergy );
1203 return *upperEnergy;
1204 }
1205
1206 return *upperEnergy;
1207}
1208
1209double PunchThroughG4Tool::interpolateEta(const double &eta, CLHEP::HepRandomEngine* rndmEngine) const{
1210
1211 double absEta = std::abs(eta);
1212
1213 ATH_MSG_DEBUG("[PunchThroughG4Tool] interpolating incoming abs(eta): " << absEta);
1214
1215 std::string etaPointsString;
1216 for (auto element:m_etaPoints){
1217 etaPointsString += std::to_string(element) + " ";
1218 }
1219
1220 ATH_MSG_DEBUG("[PunchThroughG4Tool] available eta points: " << etaPointsString);
1221
1222 auto const upperEta = std::upper_bound(m_etaPoints.begin(), m_etaPoints.end(), absEta);
1223
1224 if(upperEta == m_etaPoints.end()){
1225 ATH_MSG_DEBUG("[PunchThroughG4Tool] incoming abs(eta) > largest eta point, returning greatest eta point: " << m_etaPoints.back());
1226 return m_etaPoints.back();
1227 }
1228
1229
1230 ATH_MSG_DEBUG("[PunchThroughG4Tool] eta points upper_bound: "<< *upperEta);
1231
1232 double randomShoot = CLHEP::RandFlat::shoot(rndmEngine);
1233
1234 ATH_MSG_DEBUG("[PunchThroughG4Tool] Shooting random number: "<< randomShoot);
1235
1236 if(std::abs(absEta - *upperEta) < std::abs(absEta - *std::prev(upperEta))){
1237
1238 double distance = std::abs(absEta - *upperEta)/((*upperEta - *std::prev(upperEta))/2);
1239
1240 ATH_MSG_DEBUG( "[PunchThroughG4Tool] abs(eta) is closer to eta points upper_bound, distance: " << distance );
1241
1242 if(randomShoot > distance){
1243 ATH_MSG_DEBUG( "[PunchThroughG4Tool] randomShoot > distance, returning upper_bound " << *upperEta );
1244 return *upperEta;
1245 }
1246
1247 ATH_MSG_DEBUG( "[PunchThroughG4Tool] randomShoot < distance, returning prev(upper_bound) " << *std::prev(upperEta) );
1248
1249 return *std::prev(upperEta);
1250 }
1251 else if(std::abs(absEta - *std::prev(upperEta)) < std::abs(absEta - *upperEta)){
1252
1253 if(std::prev(upperEta) == m_etaPoints.begin()){
1254 ATH_MSG_DEBUG( "[PunchThroughG4Tool] prev of upper bound is begin, returning that: " << *std::prev(upperEta) );
1255 return *std::prev(upperEta);
1256 }
1257
1258 double distance = std::abs(absEta - *std::prev(upperEta))/((*std::prev(upperEta) - *std::prev(std::prev(upperEta)))/2);
1259
1260 ATH_MSG_DEBUG( "[PunchThroughG4Tool] abs(eta) is closer to eta points prev(upper_bound), distance: " << distance );
1261
1262 if(randomShoot > distance){
1263 ATH_MSG_DEBUG( "[PunchThroughG4Tool] randomShoot > distance, returning prev(prev(upper_bound)) " << *std::prev(std::prev(upperEta)) );
1264
1265 return *std::prev(std::prev(upperEta));
1266 }
1267 ATH_MSG_DEBUG( "[PunchThroughG4Tool] randomShoot < distance, returning prev(upper_bound) " << *std::prev(upperEta) );
1268
1269 return *std::prev(upperEta);
1270 }
1271
1272 return *std::prev(upperEta);
1273}
1274
1275StatusCode
1276PunchThroughG4Tool::registerPunchThroughParticle(G4ParticleTable &ptable, int pdg, bool doAntiparticle,
1277 double minEnergy, int maxNumParticles, double numParticlesFactor,
1278 double energyFactor, double posAngleFactor, double momAngleFactor)
1279{
1280 G4ParticleDefinition* secG4Particle = ptable.FindParticle(std::abs(pdg));
1281 double restMass = secG4Particle->GetPDGMass();
1282
1283 // read in the data needed to construct the distributions for the number of punch-through particles
1284 // (1.) get the distribution function for the number of punch-through particles
1285 std::unique_ptr<PunchThroughPDFCreator> pdf_num(readLookuptablePDF(pdg, m_fileLookupTable, "FREQ_PDG"));
1286 if (!pdf_num ) return StatusCode::FAILURE; // return error if something went wrong
1287
1288 // (2.) get the PDF for the punch-through energy
1289 std::unique_ptr<PunchThroughPDFCreator> pdf_pca0 (readLookuptablePDF(pdg, m_fileLookupTable, "PCA0_PDG"));
1290 if (!pdf_pca0)
1291 {
1292 return StatusCode::FAILURE; // return error if something went wrong
1293 }
1294
1295 // (3.) get the PDF for the punch-through particles difference in
1296 // theta compared to the incoming particle
1297 std::unique_ptr<PunchThroughPDFCreator> pdf_pca1 (readLookuptablePDF(pdg, m_fileLookupTable, "PCA1_PDG"));
1298 if (!pdf_pca1)
1299 {
1300 return StatusCode::FAILURE;
1301 }
1302
1303 // (4.) get the PDF for the punch-through particles difference in
1304 // phi compared to the incoming particle
1305 std::unique_ptr<PunchThroughPDFCreator> pdf_pca2 (readLookuptablePDF(pdg, m_fileLookupTable, "PCA2_PDG"));
1306 if (!pdf_pca2)
1307 {
1308 return StatusCode::FAILURE;
1309 }
1310
1311 // (5.) get the PDF for the punch-through particle momentum delta theta angle
1312 std::unique_ptr<PunchThroughPDFCreator> pdf_pca3 (readLookuptablePDF(pdg, m_fileLookupTable, "PCA3_PDG"));
1313 if (!pdf_pca3)
1314 {
1315 return StatusCode::FAILURE;
1316 }
1317
1318 // (6.) get the PDF for the punch-through particle momentum delta phi angle
1319 std::unique_ptr<PunchThroughPDFCreator> pdf_pca4 (readLookuptablePDF(pdg, m_fileLookupTable, "PCA4_PDG"));
1320 if (!pdf_pca4)
1321 {
1322 return StatusCode::FAILURE;
1323 }
1324
1325 // (7.) now finally store all this in the right std::map
1326 PunchThroughParticle *particle = new PunchThroughParticle(pdg, doAntiparticle);
1327 particle->setNumParticlesPDF(std::move(pdf_num));
1328 particle->setPCA0PDF(std::move(pdf_pca0));
1329 particle->setPCA1PDF(std::move(pdf_pca1));
1330 particle->setPCA2PDF(std::move(pdf_pca2));
1331 particle->setPCA3PDF(std::move(pdf_pca3));
1332 particle->setPCA4PDF(std::move(pdf_pca4));
1333
1334 // (8.) set some additional particle and simulation properties
1335 minEnergy = ( minEnergy > restMass ) ? minEnergy : restMass;
1336 particle->setMinEnergy(minEnergy);
1337 particle->setMaxNumParticles(maxNumParticles);
1338 particle->setNumParticlesFactor(numParticlesFactor);
1339 particle->setEnergyFactor(energyFactor);
1340 particle->setPosAngleFactor(posAngleFactor);
1341 particle->setMomAngleFactor(momAngleFactor);
1342
1343 // (9.) insert this PunchThroughParticle instance into the std::map class member
1344 m_particles[pdg] = particle;
1345
1346 return StatusCode::SUCCESS;
1347}
1348
1349StatusCode PunchThroughG4Tool::registerCorrelation(int pdgID1, int pdgID2,
1350 double minCorrEnergy, double fullCorrEnergy)
1351{
1352 // find the given pdgs in the registered particle ids
1353 std::map<int, PunchThroughParticle*>::iterator location1 = m_particles.find(pdgID1);
1354 std::map<int, PunchThroughParticle*>::iterator location2 = m_particles.find(pdgID2);
1355
1356 // if at least one of the given pdgs was not registered yet -> return an error
1357 if ( (location1 == m_particles.end()) || (location2 == m_particles.end()) )
1358 return StatusCode::FAILURE;
1359
1360 // now look for the correlation histograms
1361 std::stringstream name;
1362 name << "NumExitCorrelations/x_PDG" << std::abs(pdgID1) << "__y_PDG" << std::abs(pdgID2) << "__lowE";
1363 TH2F *hist1_2_lowE = (TH2F*)m_fileLookupTable->Get(name.str().c_str());
1364 name.str("");
1365 name << "NumExitCorrelations/x_PDG" << std::abs(pdgID1) << "__y_PDG" << std::abs(pdgID2) << "__highE";
1366 TH2F *hist1_2_highE = (TH2F*)m_fileLookupTable->Get(name.str().c_str());
1367 name.str("");
1368 name << "NumExitCorrelations/x_PDG" << std::abs(pdgID2) << "__y_PDG" << std::abs(pdgID1) << "__lowE";
1369 TH2F *hist2_1_lowE = (TH2F*)m_fileLookupTable->Get(name.str().c_str());
1370 name.str("");
1371 name << "NumExitCorrelations/x_PDG" << std::abs(pdgID2) << "__y_PDG" << std::abs(pdgID1) << "__highE";
1372 TH2F *hist2_1_highE = (TH2F*)m_fileLookupTable->Get(name.str().c_str());
1373 // check if the histograms exist
1374 if ( (!hist1_2_lowE) || (!hist2_1_lowE) || (!hist1_2_highE) || (!hist2_1_highE) )
1375 {
1376 ATH_MSG_ERROR("[PunchThroughG4Tool] unable to retrieve the correlation data for PDG IDs " << pdgID1 << " and " << pdgID2);
1377 return StatusCode::FAILURE;
1378 }
1379
1380 // TODO: if only one of the two histograms exists, create the other one
1381 // by mirroring the data
1382 const double lowE = getFloatAfterPatternInStr( hist1_2_lowE->GetTitle(), "elow_");
1383 const double midE = getFloatAfterPatternInStr( hist1_2_lowE->GetTitle(), "ehigh_");
1384 //TODO: check if the same:
1385 // double midE = getFloatAfterPatternInStr( hist1_2_lowE->GetTitle(), "elow_");
1386 const double upperE = getFloatAfterPatternInStr( hist1_2_highE->GetTitle(), "ehigh_");
1387 // now store the correlation either way id1->id2 and id2->id1
1388 m_particles[pdgID1]->setCorrelation(pdgID2, hist2_1_lowE, hist2_1_highE,
1389 minCorrEnergy, fullCorrEnergy,
1390 lowE, midE, upperE);
1391 m_particles[pdgID2]->setCorrelation(pdgID1, hist1_2_lowE, hist1_2_highE,
1392 minCorrEnergy, fullCorrEnergy,
1393 lowE, midE, upperE);
1394 return StatusCode::SUCCESS;
1395}
1396
1397std::unique_ptr<PunchThroughPDFCreator> PunchThroughG4Tool::readLookuptablePDF(int pdg, TFile* fileLookupTable, const std::string& folderName)
1398{
1399
1400 // will hold the PDFcreator class which will be returned at the end
1401 // this will store the distributions for the punch through particles
1402 // (as map of energy & eta of the incoming particle)
1403 //PunchThroughPDFCreator *pdf = new PunchThroughPDFCreator();
1404 std::unique_ptr<PunchThroughPDFCreator> pdf = std::make_unique<PunchThroughPDFCreator>();
1405
1406 //Get directory object
1407 std::stringstream dirName;
1408 dirName << folderName << pdg;
1409 pdf->setName(dirName.str());
1410
1411 TDirectory * dir = (TDirectory*)fileLookupTable->Get(dirName.str().c_str());
1412 if(! dir)
1413 {
1414 ATH_MSG_ERROR( "[PunchThroughG4Tool] unable to retrieve directory object ("<< folderName << pdg << ")" );
1415 return nullptr;
1416 }
1417
1418 //Get list of all objects in directory
1419 TIter keyList(dir->GetListOfKeys());
1420 TKey *key;
1421
1422 while ((key = (TKey*)keyList())) {
1423
1424 //Get histogram object from key and its name
1425 TH1* hist = nullptr;
1426
1427 std::string histName;
1428 if(strcmp(key->GetClassName(), "TH1F") == 0){
1429 hist = (TH1*)key->ReadObj();
1430 histName = hist->GetName();
1431 }
1432
1433 //extract energy and eta from hist name 6 and 1 to position delimeters correctly
1434 std::string strEnergy = histName.substr( histName.find_first_of('E') + 1, histName.find_first_of('_')-histName.find_first_of('E') - 1 );
1435 histName.erase(0, histName.find_first_of('_') + 1);
1436 std::string strEtaMin = histName.substr( histName.find("etaMin") + 6, histName.find_first_of('_') - histName.find("etaMin") - 6 );
1437 histName.erase(0, histName.find('_') + 1);
1438 std::string strEtaMax = histName.substr( histName.find("etaMax") + 6, histName.length());
1439
1440 //create integers to store in map
1441 const int energy = std::stoi(strEnergy);
1442 const int etaMin = std::stoi(strEtaMin);
1443
1444 //Add entry to pdf map
1445 pdf->addToEnergyEtaHist1DMap(energy, etaMin, hist);
1446
1447 //create doubles to store energy and eta points for interpolation
1448 const double energyDbl = static_cast<double>(energy);
1449 const double etaDbl = static_cast<double>(etaMin)/100.;
1450
1451 //create vectors to store the eta and energy points, this allows us to interpolate
1452 if (std::find(m_energyPoints.begin(), m_energyPoints.end(), energyDbl) == m_energyPoints.end()) {
1453 m_energyPoints.push_back(energyDbl);
1454 }
1455 if (std::find(m_etaPoints.begin(), m_etaPoints.end(), etaDbl) == m_etaPoints.end()) {
1456 m_etaPoints.push_back(etaDbl);
1457 }
1458
1459 }
1460
1461 return pdf;
1462}
1463
1464double PunchThroughG4Tool::getFloatAfterPatternInStr(const char *cstr, const char *cpattern)
1465{
1466 double num = 0.;
1467
1468 const std::string_view str( cstr);
1469 const std::string_view pattern( cpattern);
1470 const size_t pos = str.find(pattern);
1471
1472 if ( pos == std::string::npos)
1473 {
1474 ATH_MSG_WARNING("[PunchThroughG4Tool] unable to retrieve floating point number from string");
1475 return -999999999999.;
1476 }
1477 const std::string_view substring = str.substr(pos+pattern.length());
1478 std::from_chars(substring.data(), substring.data() + substring.size(), num);
1479 return num;
1480}
1481
1482
1483G4ThreeVector PunchThroughG4Tool::punchTroughPosPropagator(double theta, double phi, double R1, double R2, double z1, double z2) const
1484{
1485 // phi, theta - direction of the punch-through particle coming into calo
1486 // particle propagates inside the calorimeter along the straight line
1487 // coordinates of this particles when exiting the calo (on calo-MS boundary)
1488
1489 double x, y, z, r;
1490
1491 // cylinders border angles
1492 const double theta1 = atan (R1 / z1);
1493 const double theta2 = atan (R1 / z2);
1494 const double theta3 = atan (R2 / z2);
1495 //where is the particle
1496
1497 if (theta >= 0 && theta < theta1)
1498 {
1499 z = z1;
1500 r = std::fabs (z1 * tan(theta));
1501 }
1502 else if (theta >= theta1 && theta < theta2)
1503 {
1504 z = R1 / tan(theta);
1505 r = R1;
1506 }
1507 else if (theta >= theta2 && theta < theta3)
1508 {
1509 z = z2;
1510 r = std::fabs(z2 * tan(theta));;
1511 }
1512 else if (theta >= theta3 && theta < (TMath::Pi() - theta3) )
1513 {
1514 z = R2 / tan(theta);
1515 r = R2;
1516 }
1517 else if (theta >= (TMath::Pi() - theta3) && theta < (TMath::Pi() - theta2) )
1518 {
1519 z = -z2;
1520 r = std::fabs(z2 * tan(theta));
1521 }
1522 else if (theta >= (TMath::Pi() - theta2) && theta < (TMath::Pi() - theta1) )
1523 {
1524 z = R1 / tan(theta);
1525 r = R1;
1526 }
1527 else if (theta >= (TMath::Pi() - theta1) && theta <= TMath::Pi() )
1528 {
1529 z = -z1;
1530 r = std::fabs(z1 * tan(theta));
1531 }
1532
1533 //parallel universe
1534 else
1535 {
1536 ATH_MSG_WARNING("[PunchThroughG4Tool::punchTroughPosPropagator] Given theta angle is incorrect, setting particle position to (0, 0, 0)");
1537 x = 0.0; y = 0.0; z = 0.0; r = 0.0;
1538 }
1539
1540 x = r * cos(phi);
1541 y = r * sin(phi);
1542 G4ThreeVector posVec(x, y, z);
1543
1544 ATH_MSG_DEBUG("[PunchThroughG4Tool::punchTroughPosPropagator] position of produced punch-through particle: x = " << x <<" y = "<< y <<" z = "<< z<<" r = "<< posVec.perp() <<"std::sqrt(x^2+y^2) = "<< std::sqrt(x * x + y * y));
1545
1546 return posVec;
1547}
const boost::regex ref(r_ef)
#define M_PI
Scalar eta() const
pseudorapidity method
Scalar deltaPhi(const MatrixBase< Derived > &vec) const
Scalar phi() const
phi method
Scalar theta() const
theta method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
static int quant(double min, double max, unsigned nSteps, double val)
int upper(int c)
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
std::vector< T > str_to_list(const std::string_view str)
unsigned char xmlChar
#define y
#define x
#define z
std::vector< std::map< std::string, std::string > > m_xml_info_cdf
double getFloatAfterPatternInStr(const char *str, const char *pattern)
get the floating point number in a string, after the given pattern
static double normal_cdf(double x)
std::vector< std::map< std::string, std::string > > getInfoMap(const std::string &mainNode, const std::string &xmlFilePath)
virtual StatusCode initialize() override
AlgTool initialize method.
IntegerArrayProperty m_pdgInitiators
DoubleArrayProperty m_minCorrEnergy
static std::vector< double > dotProduct(const std::vector< std::vector< double > > &m, const std::vector< double > &v)
ServiceHandle< IEnvelopeDefSvc > m_envDefSvc
DoubleArrayProperty m_numParticlesFactor
DoubleArrayProperty m_initiatorsEtaRange
StatusCode registerCorrelation(int pdgID1, int pdgID2, double minCorrEnergy=0., double fullCorrEnergy=0.)
register a correlation for the two given types of punch-through particles with a given energy thresho...
std::vector< std::map< double, double > > m_variable2_inverse_cdf
TFile * m_fileLookupTable
ROOT objects.
DoubleArrayProperty m_posAngleFactor
IntegerArrayProperty m_initiatorsMinEnergy
StatusCode initializeRegisterCorrelations()
initialize register all correlations between particles
std::map< int, PunchThroughParticle * > m_particles
needed to initially create punch-through particles with the right distributions
DoubleArrayProperty m_minEnergy
std::vector< std::vector< std::vector< double > > > m_inverse_PCA_matrix
pca vectors
virtual void createAllSecondaryTracks(G4ParticleTable &ptable, G4FastStep &fastStep, const G4Track &g4PrimaryTrack, std::vector< std::map< std::string, double > > &secKinematicsMapVect, G4TrackVector &secTrackCont, const std::vector< double > &caloMSVars) override
create all secondary tracks from kinematics map
static double inverseCdfTransform(double variable, const std::map< double, double > &inverse_cdf_map)
G4Track * createSecondaryTrack(G4ParticleTable &ptable, G4FastStep &fastStep, double currentTime, int secondarySignedPDG, double energy, double theta, double phi, double momTheta, double momPhi, const std::vector< double > &caloMSVars)
create secondary track for each given the kinematics
DoubleArrayProperty m_fullCorrEnergy
std::vector< std::map< std::string, double > > checkEnergySumFromSecondaries(double mainEnergyInit, std::vector< std::map< std::string, double > > &secKinematicsMapVect)
check the energies satisfying energy condition
std::unique_ptr< PunchThroughPDFCreator > readLookuptablePDF(int pdgID, TFile *fileLookupTable, const std::string &folderName)
reads out the lookuptable for the given type of particle
IntegerArrayProperty m_correlatedParticle
StatusCode initializeInverseCDF(const std::string &quantileTransformerConfigFile)
StatusCode initializeInversePCA(const std::string &inversePCAConfigFile)
void checkParticleTable(G4ParticleTable &ptable, int secondarySignedPDG)
int getCorrelatedParticles(const G4Track &g4PrimaryTrack, std::vector< std::map< std::string, double > > &secKinematicsMapVect, int pdg, int corrParticles, CLHEP::HepRandomEngine *rndmEngine, double interpEnergy, double interpEta)
get the right number of particles for the given pdg while considering the correlation to an other par...
static std::map< double, double > getVariableCDFmappings(xmlNodePtr &nodeParent)
int passedParamIterator(int pid, double eta, const std::vector< std::map< std::string, std::string > > &mapvect) const
std::vector< std::map< double, double > > m_variable0_inverse_cdf
(vector of map) for CDF mappings
DoubleProperty m_beamPipe
beam pipe radius
std::vector< std::vector< double > > m_PCA_means
int getAllParticles(const G4Track &g4PrimaryTrack, std::vector< std::map< std::string, double > > &secKinematicsMapVect, CLHEP::HepRandomEngine *rndmEngine, int pdg, double interpEnergy, double interpEta, int numParticles=-1)
create the right number of punch-through particles for the given pdg and return the number of particl...
virtual std::vector< double > getCaloMSVars() override
double m_R1
calo-MS borders
StatusCode initializePhysics() override
double interpolateEnergy(const double &energy, CLHEP::HepRandomEngine *rndmEngine) const
StringProperty m_filenameInversePCA
StatusCode checkCaloMSBoundaries(const std::vector< std::pair< double, double > > *rzMS, const std::vector< std::pair< double, double > > *rzCalo)
Check calo-MS boundaries.
IntegerArrayProperty m_maxNumParticles
std::vector< std::map< std::string, std::string > > m_xml_info_pca
infoMaps
ServiceHandle< ISF::IGeoIDSvc > m_geoIDSvc
BooleanArrayProperty m_doAntiParticles
IntegerArrayProperty m_punchThroughParticles
virtual StatusCode finalize() override
AlgTool finalize method.
DoubleArrayProperty m_energyFactor
double interpolateEta(const double &eta, CLHEP::HepRandomEngine *rndmEngine) const
StringProperty m_filenameInverseCDF
std::map< std::string, double > getOneParticleKinematics(CLHEP::HepRandomEngine *rndmEngine, int secondaryPDG, float initParticleTheta, float initParticlePhi, double interpEnergy, double interpEta) const
create exactly one punch-through particle with the given pdg and the given max energy
std::vector< std::map< double, double > > m_variable4_inverse_cdf
StatusCode initializeRegisterPunchThroughParticles()
initialize register all the punch-through particles which will be simulated
virtual std::vector< std::map< std::string, double > > computePunchThroughParticles(const G4FastTrack &fastTrack, CLHEP::HepRandomEngine *rndmEngine, double punchThroughProbability, double punchThroughClassifierRand) override
interface function: fill a vector with the punch-through particles
DoubleArrayProperty m_momAngleFactor
std::vector< double > m_energyPoints
energy and eta points in param
StringProperty m_filenameLookupTable
std::vector< double > inversePCA(int pcaCdfIterator, std::vector< double > &variables) const
std::vector< double > m_etaPoints
PunchThroughG4Tool(const std::string &, const std::string &, const IInterface *)
StatusCode registerPunchThroughParticle(G4ParticleTable &ptable, int pdg, bool doAntiparticle=false, double minEnergy=0., int maxNumParticles=-1, double numParticlesFactor=1., double energyFactor=1., double posAngleFactor=1., double momAngleFactor=1.)
registers a type of punch-through particles which will be simulated
std::vector< std::map< double, double > > m_variable3_inverse_cdf
G4ThreeVector punchTroughPosPropagator(double theta, double phi, double R1, double R2, double z1, double z2) const
get particle through the calorimeter
std::vector< std::map< double, double > > m_variable1_inverse_cdf
This class holds information for different properties of a punch-through particle (energy,...
Definition node.h:24
void name(const std::string &n)
Definition node.h:40
int r
Definition globals.cxx:22
Eigen::Matrix< double, 3, 1 > Vector3D
AtlasRegion
A simple enum of ATLAS regions and sub-detectors.
Definition AtlasRegion.h:21
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)