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