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