CMS 3D CMS Logo

NuclearInteraction.cc
Go to the documentation of this file.
1 // system headers
2 #include <cmath>
3 #include <memory>
4 #include <vector>
5 #include <map>
6 #include <string>
7 #include <fstream>
8 #include <iostream>
9 #include <sys/stat.h>
10 #include <cmath>
11 #include <TFile.h>
12 #include <TTree.h>
13 #include <TROOT.h>
14 #include <Math/AxisAngle.h>
15 #include "Math/Boost.h"
17 
18 // Framework Headers
23 
25 
26 // Fast Sim headers
34 
35 // Math
37 
39 // Author: Patrick Janot
40 // Date: 25-Jan-2007
41 //
42 // Revision: Class structure modified to match SimplifiedGeometryPropagator
43 // Unified the way, the closest charged daughter is defined
44 // S. Kurz, 29 May 2017
46 
47 // TODO: save() function called in destructer. Does that actually make sense?
48 
51 
52 namespace fastsim {
54 
59  public:
62 
64  ~NuclearInteraction() override;
65 
67 
73  void interact(fastsim::Particle& particle,
74  const SimplifiedGeometry& layer,
75  std::vector<std::unique_ptr<fastsim::Particle> >& secondaries,
76  const RandomEngineAndDistribution& random) override;
77 
78  private:
80  unsigned index(int thePid);
81 
83  XYZVector orthogonal(const XYZVector& aVector) const;
84 
86  void save();
87 
90 
91  double theDistCut;
92  double theHadronEnergy;
94 
96  // Read/Save nuclear interactions from FullSim
98 
99  TFile* theFile = nullptr;
100  std::vector<std::vector<TTree*> > theTrees;
101  std::vector<std::vector<TBranch*> > theBranches;
102  std::vector<std::vector<NUEvent*> > theNUEvents;
103  std::vector<std::vector<unsigned> > theCurrentEntry;
104  std::vector<std::vector<unsigned> > theCurrentInteraction;
105  std::vector<std::vector<unsigned> > theNumberOfEntries;
106  std::vector<std::vector<unsigned> > theNumberOfInteractions;
107  std::vector<std::vector<std::string> > theFileNames;
108  std::vector<std::vector<double> > theHadronCM;
109 
110  unsigned ien4;
111 
112  std::ofstream myOutputFile;
113  unsigned myOutputBuffer;
114 
116 
118  // Properties of the Hadrons
120 
122  static std::vector<std::vector<double> > theRatiosMap;
124  static std::map<int, int> theIDMap;
125 
127  const std::vector<double> theHadronEN = {
128  1.0, 2.0, 3.0, 4.0, 5.0, 7.0, 9.0, 12.0, 15.0, 20.0, 30.0, 50.0, 100.0, 200.0, 300.0, 500.0, 700.0, 1000.0};
129 
130  // Use same order for all vectors below (Properties for "piplus", "piminus", "K0L", "Kplus", "Kminus", "p", "pbar", "n", "nbar")
131 
133  const std::vector<int> theHadronID = {211, -211, 130, 321, -321, 2212, -2212, 2112, -2112};
135  const std::vector<std::string> theHadronNA = {
136  "piplus", "piminus", "K0L", "Kplus", "Kminus", "p", "pbar", "n", "nbar"};
138  const std::vector<double> theHadronMA = {
139  0.13957, 0.13957, 0.497648, 0.493677, 0.493677, 0.93827, 0.93827, 0.939565, 0.939565};
141  const std::vector<double> theHadronPMin = {0.7, 0.0, 1.0, 1.0, 0.0, 1.1, 0.0, 1.1, 0.0};
143  const std::vector<double> theLengthRatio = {//pi+ pi- K0L K+ K- p pbar n nbar
144  0.2257,
145  0.2294,
146  0.3042,
147  0.2591,
148  0.2854,
149  0.3101,
150  0.5216,
151  0.3668,
152  0.4898};
154  const std::vector<double> theRatios = {// pi+ (211)
155  0.031390573,
156  0.531842852,
157  0.819614219,
158  0.951251711,
159  0.986382750,
160  1.000000000,
161  0.985087033,
162  0.982996773,
163  0.990832192,
164  0.992237923,
165  0.994841580,
166  0.973816742,
167  0.967264815,
168  0.971714258,
169  0.969122824,
170  0.978681792,
171  0.977312732,
172  0.984255819,
173  // pi- (-211)
174  0.035326512,
175  0.577356403,
176  0.857118809,
177  0.965683504,
178  0.989659360,
179  1.000000000,
180  0.989599240,
181  0.980665408,
182  0.988384816,
183  0.981038152,
184  0.975002104,
185  0.959996152,
186  0.953310808,
187  0.954705592,
188  0.957615400,
189  0.961150456,
190  0.965022184,
191  0.960573304,
192  // K0L (130)
193  0.000000000,
194  0.370261189,
195  0.649793096,
196  0.734342408,
197  0.749079499,
198  0.753360057,
199  0.755790543,
200  0.755872164,
201  0.751337674,
202  0.746685288,
203  0.747519634,
204  0.739357554,
205  0.735004444,
206  0.803039922,
207  0.832749896,
208  0.890900187,
209  0.936734805,
210  1.000000000,
211  // K+ (321)
212  0.000000000,
213  0.175571717,
214  0.391683394,
215  0.528946472,
216  0.572818635,
217  0.614210280,
218  0.644125538,
219  0.670304050,
220  0.685144573,
221  0.702870161,
222  0.714708513,
223  0.730805263,
224  0.777711536,
225  0.831090576,
226  0.869267129,
227  0.915747562,
228  0.953370523,
229  1.000000000,
230  // K- (-321)
231  0.000000000,
232  0.365353210,
233  0.611663677,
234  0.715315908,
235  0.733498956,
236  0.738361302,
237  0.745253654,
238  0.751459671,
239  0.750628335,
240  0.746442657,
241  0.750850669,
242  0.744895986,
243  0.735093960,
244  0.791663444,
245  0.828609543,
246  0.889993040,
247  0.940897842,
248  1.000000000,
249  // proton (2212)
250  0.000000000,
251  0.042849136,
252  0.459103223,
253  0.666165343,
254  0.787930873,
255  0.890397011,
256  0.920999533,
257  0.937832788,
258  0.950920131,
259  0.966595049,
260  0.979542270,
261  0.988061653,
262  0.983260159,
263  0.988958431,
264  0.991723494,
265  0.995273237,
266  1.000000000,
267  0.999962634,
268  // anti-proton (-2212)
269  1.000000000,
270  0.849956907,
271  0.775625988,
272  0.802018230,
273  0.816207485,
274  0.785899785,
275  0.754998487,
276  0.728977244,
277  0.710010673,
278  0.670890339,
279  0.665627872,
280  0.652682888,
281  0.613334247,
282  0.647534574,
283  0.667910938,
284  0.689919693,
285  0.709200185,
286  0.724199928,
287  // neutron (2112)
288  0.000000000,
289  0.059216484,
290  0.437844536,
291  0.610370629,
292  0.702090648,
293  0.780076890,
294  0.802143073,
295  0.819570432,
296  0.825829666,
297  0.840079750,
298  0.838435509,
299  0.837529986,
300  0.835687165,
301  0.885205014,
302  0.912450156,
303  0.951451221,
304  0.973215562,
305  1.000000000,
306  // anti-neutron
307  1.000000000,
308  0.849573257,
309  0.756479495,
310  0.787147094,
311  0.804572414,
312  0.791806302,
313  0.760234588,
314  0.741109531,
315  0.724118186,
316  0.692829761,
317  0.688465897,
318  0.671806061,
319  0.636461171,
320  0.675314029,
321  0.699134460,
322  0.724305037,
323  0.742556115,
324  0.758504713};
325 
327  // Used to build the ID map 'theIDMap' (i.e., what is to be considered as a proton, etc...)
329 
330  const std::vector<int> protonsID = {2212, 3222, -101, -102, -103, -104};
331  const std::vector<int> antiprotonsID = {-2212, -3222};
332  const std::vector<int> neutronsID = {2112, 3122, 3112, 3312, 3322, 3334, -3334};
333  const std::vector<int> antineutronsID = {-2112, -3122, -3112, -3312, -3322};
334  const std::vector<int> K0LsID = {130, 310};
335  const std::vector<int> KplussesID = {321};
336  const std::vector<int> KminussesID = {-321};
337  const std::vector<int> PiplussesID = {211};
338  const std::vector<int> PiminussesID = {-211};
339  };
340 
341  // TODO: Is this correct?
342  // Thread safety
343  static std::once_flag initializeOnce;
344  CMS_THREAD_GUARD(initializeOnce) std::vector<std::vector<double> > NuclearInteraction::theRatiosMap;
346 } // namespace fastsim
347 
349  : fastsim::InteractionModel(name), currentValuesWereSet(false) {
350  // Full path to FullSim root file
352 
353  // Read from config file
354  theDistCut = cfg.getParameter<double>("distCut");
355  theHadronEnergy = cfg.getParameter<double>("hadronEnergy");
356  inputFile = cfg.getUntrackedParameter<std::string>("inputFile", "");
357 
358  // The evolution of the interaction lengths with energy
359  // initialize once for all possible instances
360  std::call_once(initializeOnce, [this]() {
361  theRatiosMap.resize(theHadronID.size());
362  for (unsigned i = 0; i < theHadronID.size(); ++i) {
363  for (unsigned j = 0; j < theHadronEN.size(); ++j) {
364  theRatiosMap[i].push_back(theRatios[i * theHadronEN.size() + j]);
365  }
366  }
367 
368  // Build the ID map (i.e., what is to be considered as a proton, etc...)
369  // Protons
370  for (const auto& id : protonsID)
371  theIDMap[id] = 2212;
372  // Anti-Protons
373  for (const auto& id : antiprotonsID)
374  theIDMap[id] = -2212;
375  // Neutrons
376  for (const auto& id : neutronsID)
377  theIDMap[id] = 2112;
378  // Anti-Neutrons
379  for (const auto& id : antineutronsID)
380  theIDMap[id] = -2112;
381  // K0L's
382  for (const auto& id : K0LsID)
383  theIDMap[id] = 130;
384  // K+'s
385  for (const auto& id : KplussesID)
386  theIDMap[id] = 321;
387  // K-'s
388  for (const auto& id : KminussesID)
389  theIDMap[id] = -321;
390  // pi+'s
391  for (const auto& id : PiplussesID)
392  theIDMap[id] = 211;
393  // pi-'s
394  for (const auto& id : PiminussesID)
395  theIDMap[id] = -211;
396  });
397 
398  // Prepare the map of files
399  // Loop over the particle names
400  TFile* aVFile = nullptr;
401  std::vector<TTree*> aVTree(theHadronEN.size());
402  std::vector<TBranch*> aVBranch(theHadronEN.size());
403  std::vector<NUEvent*> aVNUEvents(theHadronEN.size());
404  std::vector<unsigned> aVCurrentEntry(theHadronEN.size());
405  std::vector<unsigned> aVCurrentInteraction(theHadronEN.size());
406  std::vector<unsigned> aVNumberOfEntries(theHadronEN.size());
407  std::vector<unsigned> aVNumberOfInteractions(theHadronEN.size());
408  std::vector<std::string> aVFileName(theHadronEN.size());
409  std::vector<double> aVHadronCM(theHadronEN.size());
410  theTrees.resize(theHadronNA.size());
411  theBranches.resize(theHadronNA.size());
412  theNUEvents.resize(theHadronNA.size());
413  theCurrentEntry.resize(theHadronNA.size());
414  theCurrentInteraction.resize(theHadronNA.size());
415  theNumberOfEntries.resize(theHadronNA.size());
416  theNumberOfInteractions.resize(theHadronNA.size());
417  theFileNames.resize(theHadronNA.size());
418  theHadronCM.resize(theHadronNA.size());
419  theFile = aVFile;
420  for (unsigned iname = 0; iname < theHadronNA.size(); ++iname) {
421  theTrees[iname] = aVTree;
422  theBranches[iname] = aVBranch;
423  theNUEvents[iname] = aVNUEvents;
424  theCurrentEntry[iname] = aVCurrentEntry;
425  theCurrentInteraction[iname] = aVCurrentInteraction;
426  theNumberOfEntries[iname] = aVNumberOfEntries;
427  theNumberOfInteractions[iname] = aVNumberOfInteractions;
428  theFileNames[iname] = aVFileName;
429  theHadronCM[iname] = aVHadronCM;
430  }
431 
432  // Read the information from a previous run (to keep reproducibility)
433  currentValuesWereSet = this->read(inputFile);
435  std::cout << "***WARNING*** You are reading nuclear-interaction information from the file " << inputFile
436  << " created in an earlier run." << std::endl;
437 
438  // Open the file for saving the information of the current run
439  myOutputFile.open("NuclearInteractionOutputFile.txt");
440  myOutputBuffer = 0;
441 
442  // Open the root file
443  edm::FileInPath myDataFile("FastSimulation/MaterialEffects/data/NuclearInteractions.root");
444  fullPath = myDataFile.fullPath();
445  theFile = TFile::Open(fullPath.c_str());
446 
447  // Open the trees
448  for (unsigned iname = 0; iname < theHadronNA.size(); ++iname) {
449  for (unsigned iene = 0; iene < theHadronEN.size(); ++iene) {
450  std::ostringstream filename;
451  double theEne = theHadronEN[iene];
452  filename << "NuclearInteractionsVal_" << theHadronNA[iname] << "_E" << theEne << ".root";
453  theFileNames[iname][iene] = filename.str();
454 
455  std::string treeName = "NuclearInteractions_" + theHadronNA[iname] + "_E" + std::to_string(int(theEne));
456  theTrees[iname][iene] = (TTree*)theFile->Get(treeName.c_str());
457 
458  if (!theTrees[iname][iene])
459  throw cms::Exception("FastSimulation/MaterialEffects") << "Tree with name " << treeName << " not found ";
460  theBranches[iname][iene] = theTrees[iname][iene]->GetBranch("nuEvent");
461  if (!theBranches[iname][iene])
462  throw cms::Exception("FastSimulation/MaterialEffects")
463  << "Branch with name nuEvent not found in " << theFileNames[iname][iene];
464 
465  theNUEvents[iname][iene] = new NUEvent();
466  theBranches[iname][iene]->SetAddress(&theNUEvents[iname][iene]);
467  theNumberOfEntries[iname][iene] = theTrees[iname][iene]->GetEntries();
468 
469  if (currentValuesWereSet) {
470  theTrees[iname][iene]->GetEntry(theCurrentEntry[iname][iene]);
471  unsigned NInteractions = theNUEvents[iname][iene]->nInteractions();
472  theNumberOfInteractions[iname][iene] = NInteractions;
473  }
474 
475  // Compute the corresponding cm energies of the nuclear interactions
476  XYZTLorentzVector Proton(0., 0., 0., 0.986);
477  XYZTLorentzVector Reference(
478  0.,
479  0.,
480  std::sqrt(theHadronEN[iene] * theHadronEN[iene] - theHadronMA[iname] * theHadronMA[iname]),
481  theHadronEN[iene]);
482  theHadronCM[iname][iene] = (Reference + Proton).M();
483  }
484  }
485 
486  // Find the index for which EN = 4. (or thereabout)
487  ien4 = 0;
488  while (theHadronEN[ien4] < 4.0)
489  ++ien4;
490 
491  gROOT->cd();
492 }
493 
495  // Close all local files
496  // Among other things, this allows the TROOT destructor to end up
497  // without crashing, while trying to close these files from outside
498  theFile->Close();
499  delete theFile;
500 
501  for (auto& vEvents : theNUEvents) {
502  for (auto evtPtr : vEvents) {
503  delete evtPtr;
504  }
505  }
506 
507  // Save data
508  save();
509  // Close the output file
510  myOutputFile.close();
511 }
512 
514  const SimplifiedGeometry& layer,
515  std::vector<std::unique_ptr<fastsim::Particle> >& secondaries,
516  const RandomEngineAndDistribution& random) {
517  int pdgId = particle.pdgId();
518  //
519  // no valid PDGid
520  //
521  if (abs(pdgId) <= 100 || abs(pdgId) >= 1000000) {
522  return;
523  }
524 
525  double radLengths = layer.getThickness(particle.position(), particle.momentum());
526  // TEC layers have some fudge factor for nuclear interactions
527  radLengths *= layer.getNuclearInteractionThicknessFactor();
528  //
529  // no material
530  //
531  if (radLengths < 1E-10) {
532  return;
533  }
534 
535  // In case the events are not read from (old) saved file, then pick a random event from FullSim file
536  if (!currentValuesWereSet) {
537  currentValuesWereSet = true;
538  for (unsigned iname = 0; iname < theHadronNA.size(); ++iname) {
539  for (unsigned iene = 0; iene < theHadronEN.size(); ++iene) {
540  theCurrentEntry[iname][iene] = (unsigned)(theNumberOfEntries[iname][iene] * random.flatShoot());
541 
542  theTrees[iname][iene]->GetEntry(theCurrentEntry[iname][iene]);
543  unsigned NInteractions = theNUEvents[iname][iene]->nInteractions();
544  theNumberOfInteractions[iname][iene] = NInteractions;
545 
546  theCurrentInteraction[iname][iene] = (unsigned)(theNumberOfInteractions[iname][iene] * random.flatShoot());
547  }
548  }
549  }
550 
551  // Momentum of interacting hadron
552  double pHadron = std::sqrt(particle.momentum().Vect().Mag2());
553 
554  //
555  // The hadron has not enough momentum to create some relevant final state
556  //
557  if (pHadron < theHadronEnergy) {
558  return;
559  }
560 
561  // The particle type
562  std::map<int, int>::const_iterator thePit = theIDMap.find(pdgId);
563  // Use map for unique ID (e.g. proton = {2212, 3222, -101, -102, -103, -104})
564  int thePid = (thePit != theIDMap.end() ? thePit->second : pdgId);
565 
566  // Is this particle type foreseen?
567  unsigned fPid = abs(thePid);
568  if (fPid != 211 && fPid != 130 && fPid != 321 && fPid != 2112 && fPid != 2212) {
569  return;
570  }
571 
572  // The hashed ID
573  unsigned thePidIndex = index(thePid);
574  // The inelastic interaction length at p(Hadron) = 5 GeV/c
575  double theInelasticLength = radLengths * theLengthRatio[thePidIndex];
576 
577  // The elastic interaction length
578  // The baroque parameterization is a fit to Fig. 40.13 of the PDG
579  double ee = pHadron > 0.6 ? exp(-std::sqrt((pHadron - 0.6) / 1.122)) : exp(std::sqrt((0.6 - pHadron) / 1.122));
580  double theElasticLength = (0.8753 * ee + 0.15) * theInelasticLength;
581 
582  // The total interaction length
583  double theTotalInteractionLength = theInelasticLength + theElasticLength;
584 
585  //
586  // Probability to interact is dl/L0 (maximum for 4 GeV Hadron)
587  //
588  double aNuclInteraction = -std::log(random.flatShoot());
589  if (aNuclInteraction > theTotalInteractionLength) {
590  return;
591  }
592 
593  // The elastic part
594  double elastic = random.flatShoot();
595  if (elastic < theElasticLength / theTotalInteractionLength) {
596  // Characteristic scattering angle for the elastic part
597  // A of silicon
598  double A = 28.0855;
599  double theta0 = std::sqrt(3.) / std::pow(A, 1. / 3.) * particle.momentum().mass() / pHadron;
600 
601  // Draw an angle with theta/theta0*exp[(-theta/2theta0)**2] shape
602  double theta = theta0 * std::sqrt(-2. * std::log(random.flatShoot()));
603  double phi = 2. * M_PI * random.flatShoot();
604 
605  // Rotate the particle accordingly
606  ROOT::Math::AxisAngle rotation1(orthogonal(particle.momentum().Vect()), theta);
607  ROOT::Math::AxisAngle rotation2(particle.momentum().Vect(), phi);
608  // Rotate!
609  XYZVector rotated = rotation2((rotation1(particle.momentum().Vect())));
610 
611  // Create a daughter if the kink is large enough
612  if (std::sin(theta) > theDistCut) {
613  secondaries.emplace_back(
615  particle.position(),
616  XYZTLorentzVector(rotated.X(), rotated.Y(), rotated.Z(), particle.momentum().E())));
617 
618  // Set the ClosestCharged Daughter thing for tracking
619  if (particle.charge() != 0) {
620  secondaries.back()->setMotherDeltaR(particle.momentum());
621  secondaries.back()->setMotherPdgId(pdgId);
622  secondaries.back()->setMotherSimTrackIndex(particle.simTrackIndex());
623  }
624 
625  // The particle is destroyed
626  particle.momentum().SetXYZT(0., 0., 0., 0.);
627  } else {
628  // If kink is small enough just rotate particle
629  particle.momentum().SetXYZT(rotated.X(), rotated.Y(), rotated.Z(), particle.momentum().E());
630  }
631  // The inelastic part
632  } else {
633  // Avoid multiple map access
634  const std::vector<double>& aHadronCM = theHadronCM[thePidIndex];
635  const std::vector<double>& aRatios = theRatiosMap[thePidIndex];
636  // Find the file with the closest c.m energy
637  // The target nucleon
638  XYZTLorentzVector Proton(0., 0., 0., 0.939);
639  // The current particle
640  const XYZTLorentzVector& Hadron = (const XYZTLorentzVector&)particle.momentum();
641  // The smallest momentum for inelastic interactions
642  double pMin = theHadronPMin[thePidIndex];
643  // The corresponding smallest four vector
644  XYZTLorentzVector Hadron0(0., 0., pMin, std::sqrt(pMin * pMin + particle.momentum().M2()));
645 
646  // The current centre-of-mass energy
647  double ecm = (Proton + Hadron).M();
648 
649  // Get the files of interest (closest c.m. energies)
650  unsigned ene1 = 0;
651  unsigned ene2 = 0;
652  // The smallest centre-of-mass energy
653  double ecm1 = (Proton + Hadron0).M();
654 
655  double ecm2 = aHadronCM[0];
656  double ratio1 = 0.;
657  double ratio2 = aRatios[0];
658  if (ecm > aHadronCM[0] && ecm < aHadronCM[aHadronCM.size() - 1]) {
659  for (unsigned ene = 1; ene < aHadronCM.size() && ecm > aHadronCM[ene - 1]; ++ene) {
660  if (ecm < aHadronCM[ene]) {
661  ene2 = ene;
662  ene1 = ene2 - 1;
663  ecm1 = aHadronCM[ene1];
664  ecm2 = aHadronCM[ene2];
665  ratio1 = aRatios[ene1];
666  ratio2 = aRatios[ene2];
667  }
668  }
669  } else if (ecm > aHadronCM[aHadronCM.size() - 1]) {
670  ene1 = aHadronCM.size() - 1;
671  ene2 = aHadronCM.size() - 2;
672  ecm1 = aHadronCM[ene1];
673  ecm2 = aHadronCM[ene2];
674  ratio1 = aRatios[ene2];
675  ratio2 = aRatios[ene2];
676  }
677 
678  // The inelastic part of the cross section depends cm energy
679  double slope = (std::log10(ecm) - std::log10(ecm1)) / (std::log10(ecm2) - std::log10(ecm1));
680  double inelastic = ratio1 + (ratio2 - ratio1) * slope;
681  double inelastic4 = pHadron < 4. ? aRatios[ien4] : 1.;
682 
683  // Simulate an inelastic interaction
684  if (elastic > 1. - (inelastic * theInelasticLength) / theTotalInteractionLength) {
685  // Avoid mutliple map access
686  std::vector<unsigned>& aCurrentInteraction = theCurrentInteraction[thePidIndex];
687  std::vector<unsigned>& aNumberOfInteractions = theNumberOfInteractions[thePidIndex];
688  std::vector<NUEvent*>& aNUEvents = theNUEvents[thePidIndex];
689 
690  // Choice of the file to read according the the log10(ecm) distance
691  // and protection against low momentum proton and neutron that never interacts
692  // (i.e., empty files)
693  unsigned ene;
694  if (random.flatShoot() < slope || aNumberOfInteractions[ene1] == 0) {
695  ene = ene2;
696  } else {
697  ene = ene1;
698  }
699 
700  // The boost characteristics
701  XYZTLorentzVector theBoost = Proton + Hadron;
702  theBoost /= theBoost.e();
703 
704  // Check we are not either at the end of an interaction bunch
705  // or at the end of a file
706  if (aCurrentInteraction[ene] == aNumberOfInteractions[ene]) {
707  std::vector<unsigned>& aCurrentEntry = theCurrentEntry[thePidIndex];
708  std::vector<unsigned>& aNumberOfEntries = theNumberOfEntries[thePidIndex];
709  std::vector<TTree*>& aTrees = theTrees[thePidIndex];
710  ++aCurrentEntry[ene];
711 
712  aCurrentInteraction[ene] = 0;
713  if (aCurrentEntry[ene] == aNumberOfEntries[ene]) {
714  aCurrentEntry[ene] = 0;
715  }
716 
717  unsigned myEntry = aCurrentEntry[ene];
718  aTrees[ene]->GetEntry(myEntry);
719  aNumberOfInteractions[ene] = aNUEvents[ene]->nInteractions();
720  }
721 
722  // Read the interaction
723  NUEvent::NUInteraction anInteraction = aNUEvents[ene]->theNUInteractions()[aCurrentInteraction[ene]];
724 
725  unsigned firstTrack = anInteraction.first;
726  unsigned lastTrack = anInteraction.last;
727 
728  // Some rotation around the boost axis, for more randomness
729  XYZVector theAxis = theBoost.Vect().Unit();
730  double theAngle = random.flatShoot() * 2. * M_PI;
731  ROOT::Math::AxisAngle axisRotation(theAxis, theAngle);
732  ROOT::Math::Boost axisBoost(theBoost.x(), theBoost.y(), theBoost.z());
733 
734  // A rotation to bring the particles back to the Hadron direction
735  XYZVector zAxis(0., 0., 1.);
736  XYZVector orthAxis = (zAxis.Cross(theBoost.Vect())).Unit();
737  double orthAngle = acos(theBoost.Vect().Unit().Z());
738  ROOT::Math::AxisAngle orthRotation(orthAxis, orthAngle);
739 
740  // Loop on the nuclear interaction products
741  for (unsigned iTrack = firstTrack; iTrack <= lastTrack; ++iTrack) {
742  NUEvent::NUParticle aParticle = aNUEvents[ene]->theNUParticles()[iTrack];
743 
744  // Add a RawParticle with the proper energy in the c.m. frame of
745  // the nuclear interaction
746  double energy = std::sqrt(aParticle.px * aParticle.px + aParticle.py * aParticle.py +
747  aParticle.pz * aParticle.pz + aParticle.mass * aParticle.mass / (ecm * ecm));
748 
749  XYZTLorentzVector daugtherMomentum(aParticle.px * ecm, aParticle.py * ecm, aParticle.pz * ecm, energy * ecm);
750 
751  // Rotate to the collision axis
752  XYZVector rotated = orthRotation(daugtherMomentum.Vect());
753  // Rotate around the boost axis for more randomness
754  rotated = axisRotation(rotated);
755 
756  // Rotated the daughter
757  daugtherMomentum.SetXYZT(rotated.X(), rotated.Y(), rotated.Z(), daugtherMomentum.E());
758 
759  // Boost it in the lab frame
760  daugtherMomentum = axisBoost(daugtherMomentum);
761 
762  // Create secondary
763  secondaries.emplace_back(new fastsim::Particle(aParticle.id, particle.position(), daugtherMomentum));
764 
765  // The closestCharged Daughter thing for tracking
766  // BUT: 'aParticle' also has to be charged, only then the mother should be set
767  // Unfortunately, NUEvent::NUParticle does not contain any info about the charge
768  // Did some tests and effect is absolutely negligible!
769  if (particle.charge() != 0) {
770  secondaries.back()->setMotherDeltaR(particle.momentum());
771  secondaries.back()->setMotherPdgId(pdgId);
772  secondaries.back()->setMotherSimTrackIndex(particle.simTrackIndex());
773  }
774  }
775 
776  // The particle is destroyed
777  particle.momentum().SetXYZT(0., 0., 0., 0.);
778 
779  // This is a note from previous version of code but I don't understand it:
780  // ERROR The way this loops through the events breaks
781  // replay. Which events are retrieved depends on
782  // which previous events were processed.
783 
784  // Increment for next time
785  ++aCurrentInteraction[ene];
786 
787  // Simulate a stopping hadron (low momentum)
788  } else if (pHadron < 4. && elastic > 1. - (inelastic4 * theInelasticLength) / theTotalInteractionLength) {
789  // The particle is destroyed
790  particle.momentum().SetXYZT(0., 0., 0., 0.);
791  }
792  }
793 }
794 
796  // Size of buffer
797  ++myOutputBuffer;
798 
799  // Periodically close the current file and open a new one
800  if (myOutputBuffer / 1000 * 1000 == myOutputBuffer) {
801  myOutputFile.close();
802  myOutputFile.open("NuclearInteractionOutputFile.txt");
803  }
804  //
805  unsigned size1 = theCurrentEntry.size() * theCurrentEntry.begin()->size();
806  std::vector<unsigned> theCurrentEntries;
807  theCurrentEntries.resize(size1);
808  size1 *= sizeof(unsigned);
809  //
810  unsigned size2 = theCurrentInteraction.size() * theCurrentInteraction.begin()->size();
811  std::vector<unsigned> theCurrentInteractions;
812  theCurrentInteractions.resize(size2);
813  size2 *= sizeof(unsigned);
814 
815  // Save the current entries
816  std::vector<std::vector<unsigned> >::const_iterator aCurrentEntry = theCurrentEntry.begin();
817  std::vector<std::vector<unsigned> >::const_iterator lastCurrentEntry = theCurrentEntry.end();
818  unsigned allEntries = 0;
819  for (; aCurrentEntry != lastCurrentEntry; ++aCurrentEntry) {
820  unsigned size = aCurrentEntry->size();
821  for (unsigned iene = 0; iene < size; ++iene)
822  theCurrentEntries[allEntries++] = (*aCurrentEntry)[iene];
823  }
824 
825  // Save the current interactions
826  std::vector<std::vector<unsigned> >::const_iterator aCurrentInteraction = theCurrentInteraction.begin();
827  std::vector<std::vector<unsigned> >::const_iterator lastCurrentInteraction = theCurrentInteraction.end();
828  unsigned allInteractions = 0;
829  for (; aCurrentInteraction != lastCurrentInteraction; ++aCurrentInteraction) {
830  unsigned size = aCurrentInteraction->size();
831  for (unsigned iene = 0; iene < size; ++iene)
832  theCurrentInteractions[allInteractions++] = (*aCurrentInteraction)[iene];
833  }
834  // Write to file
835  myOutputFile.write((const char*)(&theCurrentEntries.front()), size1);
836  myOutputFile.write((const char*)(&theCurrentInteractions.front()), size2);
837  myOutputFile.flush();
838 }
839 
841  std::ifstream myInputFile;
842  struct stat results;
843  //
844  unsigned size1 = theCurrentEntry.size() * theCurrentEntry.begin()->size();
845  std::vector<unsigned> theCurrentEntries;
846  theCurrentEntries.resize(size1);
847  size1 *= sizeof(unsigned);
848  //
849  unsigned size2 = theCurrentInteraction.size() * theCurrentInteraction.begin()->size();
850  std::vector<unsigned> theCurrentInteractions;
851  theCurrentInteractions.resize(size2);
852  size2 *= sizeof(unsigned);
853  //
854  unsigned size = 0;
855 
856  // Open the file (if any), otherwise return false
857  myInputFile.open(inputFile.c_str());
858  if (myInputFile.is_open()) {
859  // Get the size of the file
860  if (stat(inputFile.c_str(), &results) == 0)
861  size = results.st_size;
862  else
863  return false; // Something is wrong with that file !
864 
865  // Position the pointer just before the last record
866  myInputFile.seekg(size - size1 - size2);
867  myInputFile.read((char*)(&theCurrentEntries.front()), size1);
868  myInputFile.read((char*)(&theCurrentInteractions.front()), size2);
869  myInputFile.close();
870 
871  // Read the current entries
872  std::vector<std::vector<unsigned> >::iterator aCurrentEntry = theCurrentEntry.begin();
873  std::vector<std::vector<unsigned> >::iterator lastCurrentEntry = theCurrentEntry.end();
874  unsigned allEntries = 0;
875  for (; aCurrentEntry != lastCurrentEntry; ++aCurrentEntry) {
876  unsigned size = aCurrentEntry->size();
877  for (unsigned iene = 0; iene < size; ++iene)
878  (*aCurrentEntry)[iene] = theCurrentEntries[allEntries++];
879  }
880 
881  // Read the current interactions
882  std::vector<std::vector<unsigned> >::iterator aCurrentInteraction = theCurrentInteraction.begin();
883  std::vector<std::vector<unsigned> >::iterator lastCurrentInteraction = theCurrentInteraction.end();
884  unsigned allInteractions = 0;
885  for (; aCurrentInteraction != lastCurrentInteraction; ++aCurrentInteraction) {
886  unsigned size = aCurrentInteraction->size();
887  for (unsigned iene = 0; iene < size; ++iene)
888  (*aCurrentInteraction)[iene] = theCurrentInteractions[allInteractions++];
889  }
890 
891  return true;
892  }
893 
894  return false;
895 }
896 
898  // Find hashed particle ID
899  unsigned myIndex = 0;
900  while (thePid != theHadronID[myIndex])
901  ++myIndex;
902  return myIndex;
903 }
904 
906  double x = fabs(aVector.X());
907  double y = fabs(aVector.Y());
908  double z = fabs(aVector.Z());
909 
910  if (x < y)
911  return x < z ? XYZVector(0., aVector.Z(), -aVector.Y()) : XYZVector(aVector.Y(), -aVector.X(), 0.);
912  else
913  return y < z ? XYZVector(-aVector.Z(), 0., aVector.X()) : XYZVector(aVector.Y(), -aVector.X(), 0.);
914 }
915 
const std::vector< int > antineutronsID
PdgID of anti-neutrons.
size
Write out results.
const std::vector< int > PiplussesID
PdgID of pt+.
std::vector< std::vector< unsigned > > theCurrentInteraction
Necessary to read the FullSim interactions.
Implementation of a generic detector layer (base class for forward/barrel layers).
double theDistCut
Cut on deltaR for the FastSim Tracking (ClosestChargedDaughter algorithm)
const std::vector< int > KminussesID
PdgID of K-.
std::string fullPath() const
Definition: FileInPath.cc:161
static const double slope[3]
Sin< T >::type sin(const T &t)
Definition: Sin.h:22
std::vector< std::vector< unsigned > > theNumberOfEntries
Necessary to read the FullSim interactions.
bool currentValuesWereSet
Read data from file that was created in a previous run.
const math::XYZTLorentzVector & position() const
Return position of the particle.
Definition: Particle.h:140
const std::vector< int > KplussesID
PdgID of K+.
std::vector< std::vector< std::string > > theFileNames
Necessary to read the FullSim interactions.
TFile * theFile
Necessary to read the FullSim interactions.
static std::vector< std::vector< double > > theRatiosMap
The evolution of the interaction lengths with energy.
std::vector< std::vector< double > > theHadronCM
Necessary to read the FullSim interactions.
std::vector< std::vector< NUEvent * > > theNUEvents
Necessary to read the FullSim interactions.
static std::string to_string(const XMLCh *ch)
Implementation of nuclear interactions of hadrons in the tracker layers (based on fully simulated int...
const std::vector< int > protonsID
PdgID of protons.
const std::vector< double > theLengthRatio
Inelastic interaction length at p(Hadron) = 5 GeV/c (relativ to radionLength of material) ...
XYZTLorentzVectorD XYZTLorentzVector
Lorentz vector with cylindrical internal representation using pseudorapidity.
Definition: LorentzVector.h:29
Base class for any interaction model between a particle and a tracker layer.
const std::vector< double > theRatios
Filled into &#39;theRatiosMap&#39; (evolution of the interaction lengths with energy)
void interact(fastsim::Particle &particle, const SimplifiedGeometry &layer, std::vector< std::unique_ptr< fastsim::Particle > > &secondaries, const RandomEngineAndDistribution &random) override
Perform the interaction.
const std::vector< int > K0LsID
PdgID of K0.
Definition: NUEvent.h:6
const std::vector< int > antiprotonsID
PdgID of anti-protons.
static std::map< int, int > theIDMap
Build the ID map (i.e., what is to be considered as a proton, etc...)
T sqrt(T t)
Definition: SSEVec.h:19
~NuclearInteraction() override
Default destructor.
unsigned index(int thePid)
Return a hashed index for a given particle ID.
#define CMS_THREAD_GUARD(_var_)
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
int simTrackIndex() const
Return index of the SimTrack.
Definition: Particle.h:153
std::ofstream myOutputFile
Output file to save interactions.
math::XYZTLorentzVector XYZTLorentzVector
const std::vector< int > neutronsID
PdgID of neutrons.
#define M_PI
void save()
Save the nuclear interactions to a file, so you can reproduce the events (e.g. in case of a crash)...
double theHadronEnergy
Minimum energy for nuclear interaction.
int pdgId() const
Return pdgId of the particle.
Definition: Particle.h:134
math::XYZVector XYZVector
NuclearInteraction(const std::string &name, const edm::ParameterSet &cfg)
Constructor.
XYZVector orthogonal(const XYZVector &aVector) const
Return an orthogonal vector.
XYZVectorD XYZVector
spatial vector with cartesian internal representation
Definition: Vector3D.h:31
bool read(std::string inputFile)
Read the nuclear interactions from a file, so you can reproduce the events (e.g. in case of a crash)...
const std::vector< double > theHadronPMin
Smallest momentum for inelastic interactions.
const math::XYZTLorentzVector & momentum() const
Return momentum of the particle.
Definition: Particle.h:143
unsigned ien4
Find the index for which EN = 4.
double charge() const
Return charge of the particle.
Definition: Particle.h:137
const std::vector< double > theHadronMA
Masses of the hadrons.
std::vector< std::vector< TBranch * > > theBranches
Necessary to read the FullSim interactions.
const std::vector< int > PiminussesID
PdgID of pi-.
const std::vector< double > theHadronEN
Filled into &#39;theRatiosMap&#39; (evolution of the interaction lengths with energy)
std::vector< std::vector< unsigned > > theNumberOfInteractions
Necessary to read the FullSim interactions.
static std::once_flag initializeOnce
const std::vector< int > theHadronID
ID of the hadrons.
results
Definition: mysort.py:8
#define DEFINE_EDM_PLUGIN(factory, type, name)
Definition: APVGainStruct.h:7
std::string inputFile
Directory/Name of input file in case you want to read interactions from file.
double flatShoot(double xmin=0.0, double xmax=1.0) const
Definition of a generic FastSim Particle which can be propagated through the detector (formerly Parti...
Definition: Particle.h:16
std::vector< std::vector< TTree * > > theTrees
Necessary to read the FullSim interactions.
math::XYZVector XYZVector
Definition: RawParticle.h:26
const std::vector< std::string > theHadronNA
Names of the hadrons.
Geom::Theta< T > theta() const
save
Definition: cuy.py:1164
Power< A, B >::type pow(const A &a, const B &b)
Definition: Power.h:29
math::XYZTLorentzVector XYZTLorentzVector
Definition: RawParticle.h:25
std::vector< std::vector< unsigned > > theCurrentEntry
Necessary to read the FullSim interactions.
unsigned myOutputBuffer
Needed to save interactions to file.