LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
RadioGen_module.cc
Go to the documentation of this file.
1 // Rn222 generation feature added by gleb.sinev@duke.edu
9 // (based on a generator by jason.stock@mines.sdsmt.edu)
10 // JStock. Added preliminary changes to get ready for Ar42 implimentation. This includes allowing for multiple particles from the same decay.
11 // Ar42 generation added by JStock (jason.stock@mines.sdsmt.edu).
12 // Ar42 is designed to handle 5 separate different
13 // beta decay modes, each with it's own chains of
14 // possible dexcitation gammas. Because of the
15 // high energies, and the relatively high rate
16 // expected in the DUNE FD, these chains are
17 // completely simulated instead of relying on the
18 // existing machinery in this module. To make the
19 // treatment of multiple decay products and
20 // dexcitation chains generally available would
21 // require a significant redesign of the module
22 // and possibly of the .root files data structure
23 // for each radiological.
24 //
25 // Dec 01, 2017 JStock
26 // Adding the ability to make 8.997 MeV Gammas for
27 // Ni59 Calibration sources. This is another "hacky"
28 // fix to something that really deserves a more
29 // elegant and comprehensive solution.
31 #ifndef EVGEN_RADIOLOGICAL
32 #define EVGEN_RADIOLOGICAL
33 
34 // C++ includes.
35 #include <iostream>
36 #include <sstream>
37 #include <string>
38 #include <regex>
39 #include <cmath>
40 #include <memory>
41 #include <iterator>
42 #include <vector>
43 #include <sys/stat.h>
44 #include <TGeoManager.h>
45 #include <TGeoMaterial.h>
46 #include <TGeoNode.h>
47 
48 // Framework includes
51 #include "fhiclcpp/ParameterSet.h"
59 #include "cetlib_except/exception.h"
60 #include "cetlib/search_path.h"
61 
62 // art extensions
64 
65 // nutools includes
69 
70 // lar includes
73 
74 // root includes
75 
76 #include "TLorentzVector.h"
77 #include "TVector3.h"
78 #include "TGenPhaseSpace.h"
79 #include "TMath.h"
80 #include "TFile.h"
81 #include "TH1.h"
82 #include "TH1D.h"
83 #include "TGraph.h"
84 
85 #include "CLHEP/Random/RandFlat.h"
86 #include "CLHEP/Random/RandPoisson.h"
87 
88 namespace simb { class MCTruth; }
89 
90 namespace evgen {
91 
94 
95 
96 
97  class RadioGen : public art::EDProducer {
98 
99  public:
100  explicit RadioGen(fhicl::ParameterSet const& pset);
101  virtual ~RadioGen();
102 
103  // This is called for each event.
104  void produce(art::Event& evt);
105  void beginRun(art::Run& run);
106  void reconfigure(fhicl::ParameterSet const& p);
107 
108  private:
109 
110  typedef int ti_PDGID; // These typedefs may look odd, and unecessary. I chose to use them to make the tuples I use later more readable. ti, type integer :JStock
111  typedef double td_Mass; // These typedefs may look odd, and unecessary. I chose to use them to make the tuples I use later more readable. td, type double :JStock
112 
113  void SampleOne(unsigned int i,
114  simb::MCTruth &mct);
115 
116  TLorentzVector dirCalc(double p, double m);
117 
118  void readfile(std::string nuclide, std::string filename);
119  void samplespectrum(std::string nuclide, int &itype, double &t, double &m, double &p);
120 
121  void Ar42Gamma2(std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>>& v_prods);
122  void Ar42Gamma3(std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>>& v_prods);
123  void Ar42Gamma4(std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>>& v_prods);
124  void Ar42Gamma5(std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>>& v_prods);
125 
126  // recoded so as to use the LArSoft-managed random number generator
127  double samplefromth1d(TH1D *hist);
128 
129  // itype = pdg code: 1000020040: alpha. itype=11: beta. -11: positron, itype=22: gamma. -1: error
130  // t is the kinetic energy in GeV (= E-m)
131  // m = mass in GeV
132  // p = momentum in GeV
133 
134  //double betaphasespace(double mass, double q); // older parameterization.
135 
136  // the generator randomly samples points in a rectangular prism of space and time, and only selects those points in
137  // volumes with materials that match the regexes in fMaterial. One can use wildcards * and ? for broader matches.
138 
139  std::vector<std::string> fNuclide;
140  std::vector<std::string> fMaterial;
141  std::vector<double> fBq;
142  std::vector<double> fT0;
143  std::vector<double> fT1;
144  std::vector<double> fX0;
145  std::vector<double> fY0;
146  std::vector<double> fZ0;
147  std::vector<double> fX1;
148  std::vector<double> fY1;
149  std::vector<double> fZ1;
151 
152 
153  // leftovers from the phase space generator
154  // const double gevperamu = 0.931494061;
155  // TGenPhaseSpace rg; // put this here so we don't constantly construct and destruct it
156 
157  const double m_e = 0.000510998928; // mass of electron in GeV
158  const double m_alpha = 3.727379240; // mass of an alpha particle in GeV
159  const double m_neutron = 0.9395654133; // mass of a neutron in GeV
160 
161  std::vector<std::string> spectrumname;
162  std::vector<TH1D*> alphaspectrum;
163  std::vector<double> alphaintegral;
164  std::vector<TH1D*> betaspectrum;
165  std::vector<double> betaintegral;
166  std::vector<TH1D*> gammaspectrum;
167  std::vector<double> gammaintegral;
168  std::vector<TH1D*> neutronspectrum;
169  std::vector<double> neutronintegral;
170 
171  };
172 }
173 
174 namespace evgen{
175 
176  //____________________________________________________________________________
177  RadioGen::RadioGen(fhicl::ParameterSet const& pset)
178  {
179 
180  this->reconfigure(pset);
181 
182  // create a default random engine; obtain the random seed from NuRandomService,
183  // unless overridden in configuration with key "Seed"
185  ->createEngine(*this, pset, "Seed");
186 
187  produces< std::vector<simb::MCTruth> >();
188  produces< sumdata::RunData, art::InRun >();
189 
190  }
191 
192  //____________________________________________________________________________
193  RadioGen::~RadioGen()
194  {
195  }
196 
197  //____________________________________________________________________________
198  void RadioGen::reconfigure(fhicl::ParameterSet const& p)
199  {
200  // do not put seed in reconfigure because we don't want to reset
201  // the seed midstream -- same as SingleGen
202 
203  fNuclide = p.get< std::vector<std::string>>("Nuclide");
204  fMaterial = p.get< std::vector<std::string>>("Material");
205  fBq = p.get< std::vector<double> >("BqPercc");
206  fT0 = p.get< std::vector<double> >("T0");
207  fT1 = p.get< std::vector<double> >("T1");
208  fX0 = p.get< std::vector<double> >("X0");
209  fY0 = p.get< std::vector<double> >("Y0");
210  fZ0 = p.get< std::vector<double> >("Z0");
211  fX1 = p.get< std::vector<double> >("X1");
212  fY1 = p.get< std::vector<double> >("Y1");
213  fZ1 = p.get< std::vector<double> >("Z1");
214 
215  // check for consistency of vector sizes
216 
217  unsigned int nsize = fNuclide.size();
218  if ( fMaterial.size() != nsize ) throw cet::exception("RadioGen") << "Different size Material vector and Nuclide vector\n";
219  if ( fBq.size() != nsize ) throw cet::exception("RadioGen") << "Different size Bq vector and Nuclide vector\n";
220  if ( fT0.size() != nsize ) throw cet::exception("RadioGen") << "Different size T0 vector and Nuclide vector\n";
221  if ( fT1.size() != nsize ) throw cet::exception("RadioGen") << "Different size T1 vector and Nuclide vector\n";
222  if ( fX0.size() != nsize ) throw cet::exception("RadioGen") << "Different size X0 vector and Nuclide vector\n";
223  if ( fY0.size() != nsize ) throw cet::exception("RadioGen") << "Different size Y0 vector and Nuclide vector\n";
224  if ( fZ0.size() != nsize ) throw cet::exception("RadioGen") << "Different size Z0 vector and Nuclide vector\n";
225  if ( fX1.size() != nsize ) throw cet::exception("RadioGen") << "Different size X1 vector and Nuclide vector\n";
226  if ( fY1.size() != nsize ) throw cet::exception("RadioGen") << "Different size Y1 vector and Nuclide vector\n";
227  if ( fZ1.size() != nsize ) throw cet::exception("RadioGen") << "Different size Z1 vector and Nuclide vector\n";
228 
229  for(std::string & nuclideName : fNuclide){
230  if(nuclideName=="39Ar" ){readfile("39Ar","Argon_39.root") ;}
231  else if(nuclideName=="60Co" ){readfile("60Co","Cobalt_60.root") ;}
232  else if(nuclideName=="85Kr" ){readfile("85Kr","Krypton_85.root") ;}
233  else if(nuclideName=="40K" ){readfile("40K","Potassium_40.root") ;}
234  else if(nuclideName=="232Th"){readfile("232Th","Thorium_232.root");}
235  else if(nuclideName=="238U" ){readfile("238U","Uranium_238.root") ;}
236  else if(nuclideName=="222Rn"){continue;} //Rn222 is handeled separately later
237  else if(nuclideName=="59Ni"){continue;} //Rn222 is handeled separately later
238  else if(nuclideName=="42Ar" ){
239  readfile("42Ar_1", "Argon_42_1.root"); //Each possible beta decay mode of Ar42 is given it's own .root file for now.
240  readfile("42Ar_2", "Argon_42_2.root"); //This allows us to know which decay chain to follow for the dexcitation gammas.
241  readfile("42Ar_3", "Argon_42_3.root"); //The dexcitation gammas are not included in the root files as we want to
242  readfile("42Ar_4", "Argon_42_4.root"); //probabilistically simulate the correct coincident gammas, which we cannot guarantee
243  readfile("42Ar_5", "Argon_42_5.root"); //by sampling a histogram.
244  continue;
245  } //Ar42 is handeled separately later
246  else{
247  std::string searchName = nuclideName;
248  searchName+=".root";
249  readfile(nuclideName,searchName);
250  }
251  }
252 
253  return;
254  }
255 
256  //____________________________________________________________________________
257  void RadioGen::beginRun(art::Run& run)
258  {
259 
260  // grab the geometry object to see what geometry we are using
261 
263  std::unique_ptr<sumdata::RunData> runcol(new sumdata::RunData(geo->DetectorName()));
264  run.put(std::move(runcol));
265 
266  return;
267  }
268 
269  //____________________________________________________________________________
270  void RadioGen::produce(art::Event& evt)
271  {
272 
274  std::unique_ptr< std::vector<simb::MCTruth> > truthcol(new std::vector<simb::MCTruth>);
275 
276  simb::MCTruth truth;
278 
279  trackidcounter = -1;
280  for (unsigned int i=0; i<fNuclide.size(); ++i) {
281  SampleOne(i,truth);
282  }//end loop over nuclides
283 
284  LOG_DEBUG("RadioGen") << truth;
285  truthcol->push_back(truth);
286  evt.put(std::move(truthcol));
287  return;
288  }
289 
290  //____________________________________________________________________________
291  // Generate radioactive decays per nuclide per volume according to the FCL parameters
292 
293  void RadioGen::SampleOne(unsigned int i, simb::MCTruth &mct){
294 
296  TGeoManager *geomanager = geo->ROOTGeoManager();
297 
298  // get the random number generator service and make some CLHEP generators
300  CLHEP::HepRandomEngine &engine = rng->getEngine();
301  CLHEP::RandFlat flat(engine);
302  CLHEP::RandPoisson poisson(engine);
303 
304  // figure out how many decays to generate, assuming that the entire prism consists of the radioactive material.
305  // we will skip over decays in other materials later.
306 
307  double rate = fabs( fBq[i] * (fT1[i] - fT0[i]) * (fX1[i] - fX0[i]) * (fY1[i] - fY0[i]) * (fZ1[i] - fZ0[i]) ) / 1.0E9;
308  long ndecays = poisson.shoot(rate);
309 
310  for (unsigned int idecay=0; idecay<ndecays; idecay++)
311  {
312  // generate just one particle at a time. Need a little recoding if a radioactive
313  // decay generates multiple daughters that need simulation
314  // uniformly distributed in position and time
315  //
316  // JStock: Leaving this as a single position for the decay products. For now I will assume they all come from the same spot.
317  TLorentzVector pos( fX0[i] + flat.fire()*(fX1[i] - fX0[i]),
318  fY0[i] + flat.fire()*(fY1[i] - fY0[i]),
319  fZ0[i] + flat.fire()*(fZ1[i] - fZ0[i]),
320  fT0[i] + flat.fire()*(fT1[i] - fT0[i]) );
321 
322  // discard decays that are not in the proper material
323  std::string volmaterial = geomanager->FindNode(pos.X(),pos.Y(),pos.Z())->GetMedium()->GetMaterial()->GetName();
324  //mf::LogDebug("RadioGen") << "Position: " << pos.X() << " " << pos.Y() << " " << pos.Z() << " " << pos.T() << " " << volmaterial << " " << fMaterial[i] << std::endl;
325  if ( ! std::regex_match(volmaterial, std::regex(fMaterial[i])) ) continue;
326  //mf::LogDebug("RadioGen") << "Decay accepted" << std::endl;
327 
328  //Moved pdgid into the next statement, so that it is localized.
329  // electron=11, photon=22, alpha = 1000020040, neutron = 2112
330 
331  //JStock: Allow us to have different particles from the same decay. This requires multiple momenta.
332  std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>> v_prods; //(First is for PDGID, second is mass, third is Momentum)
333 
334  if (fNuclide[i] == "222Rn") // Treat 222Rn separately
335  {
336  double p=0; double t=0.00548952; td_Mass m=m_alpha; ti_PDGID pdgid=1000020040; //td_Mass = double. ti_PDGID = int;
337  double energy = t + m;
338  double p2 = energy*energy - m*m;
339  if (p2 > 0) p = TMath::Sqrt(p2);
340  else p = 0;
341  //Make TLorentzVector and push it to the back of v_prods.
342  std::tuple<ti_PDGID, td_Mass, TLorentzVector> partMassMom = std::make_tuple(pdgid, m, dirCalc(p,m));
343  v_prods.push_back(partMassMom);
344  }//End special case RN222
345  else if(fNuclide[i] == "59Ni"){ //Treat 59Ni Calibration Source separately (as I haven't made a spectrum for it, and ultimately it should be handeled with multiple particle outputs.
346  double p=0.008997; td_Mass m=0; ti_PDGID pdgid=22; // td_Mas=double. ti_PDFID=int. Assigning p directly, as t=p for gammas.
347  std::tuple<ti_PDGID, td_Mass, TLorentzVector> partMassMom = std::make_tuple(pdgid, m, dirCalc(p,m));
348  v_prods.push_back(partMassMom);
349  }//end special case Ni59 calibration source
350  else if(fNuclide[i] == "42Ar"){ // Spot for special treatment of Ar42.
351  double p=0; double t=0; td_Mass m = 0; ti_PDGID pdgid=0; //td_Mass = double. ti_PDGID = int;
352  double bSelect = flat.fire(); //Make this a random number from 0 to 1.
353  if(bSelect<0.819){ //beta channel 1. No Gamma. beta Q value 3525.22 keV
354  samplespectrum("42Ar_1", pdgid, t, m, p);
355  std::tuple<ti_PDGID, td_Mass, TLorentzVector> partMassMom = std::make_tuple(pdgid, m, dirCalc(p,m));
356  v_prods.push_back(partMassMom);
357  //No gamma here.
358  }else if(bSelect<0.9954){ //beta channel 2. 1 Gamma (1524.6 keV). beta Q value 2000.62
359  samplespectrum("42Ar_2", pdgid, t, m, p);
360  std::tuple<ti_PDGID, td_Mass, TLorentzVector> partMassMom = std::make_tuple(pdgid, m, dirCalc(p,m));
361  v_prods.push_back(partMassMom);
362  Ar42Gamma2(v_prods);
363  }else if(bSelect<0.9988){ //beta channel 3. 1 Gamma Channel. 312.6 keV + gamma 2. beta Q value 1688.02 keV
364  samplespectrum("42Ar_3", pdgid, t, m, p);
365  std::tuple<ti_PDGID, td_Mass, TLorentzVector> partMassMom = std::make_tuple(pdgid, m, dirCalc(p,m));
366  v_prods.push_back(partMassMom);
367  Ar42Gamma3(v_prods);
368  }else if(bSelect<0.9993){ //beta channel 4. 2 Gamma Channels. Either 899.7 keV (i 0.052) + gamma 2 or 2424.3 keV (i 0.020). beta Q value 1100.92 keV
369  samplespectrum("42Ar_4", pdgid, t, m, p);
370  std::tuple<ti_PDGID, td_Mass, TLorentzVector> partMassMom = std::make_tuple(pdgid, m, dirCalc(p,m));
371  v_prods.push_back(partMassMom);
372  Ar42Gamma4(v_prods);
373  }else{ //beta channel 5. 3 gamma channels. 692.0 keV + 1228.0 keV + Gamma 2 (i 0.0033) ||OR|| 1021.2 keV + gamma 4 (i 0.0201) ||OR|| 1920.8 keV + gamma 2 (i 0.041). beta Q value 79.82 keV
374  samplespectrum("42Ar_5", pdgid, t, m, p);
375  std::tuple<ti_PDGID, td_Mass, TLorentzVector> partMassMom = std::make_tuple(pdgid, m, dirCalc(p,m));
376  v_prods.push_back(partMassMom);
377  Ar42Gamma5(v_prods);
378  }
379  //Add beta.
380  //Call gamma function for beta mode.
381  }
382  else{ //General Case.
383  double p=0; double t=0; td_Mass m = 0; ti_PDGID pdgid=0; //td_Mass = double. ti_PDGID = int;
384  samplespectrum(fNuclide[i],pdgid,t,m,p);
385  std::tuple<ti_PDGID, td_Mass, TLorentzVector> partMassMom = std::make_tuple(pdgid, m, dirCalc(p,m));
386  v_prods.push_back(partMassMom);
387  }//end else (not RN or other special case
388 
389  //JStock: Modify this to now loop over the v_prods.
390  for(auto prodEntry : v_prods){
391  // set track id to a negative serial number as these are all primary particles and have id <= 0
392  int trackid = trackidcounter;
393  ti_PDGID pdgid = std::get<0>(prodEntry);
394  td_Mass m = std::get<1>(prodEntry);
395  TLorentzVector pvec = std::get<2>(prodEntry);
396  trackidcounter--;
397  std::string primary("primary");
398 
399  // alpha particles need a little help since they're not in the TDatabasePDG table
400  // // so don't rely so heavily on default arguments to the MCParticle constructor
401  if (pdgid == 1000020040){
402  simb::MCParticle part(trackid, pdgid, primary,-1,m,1);
403  part.AddTrajectoryPoint(pos, pvec);
404  mct.Add(part);
405  }// end "If alpha"
406  else{
407  simb::MCParticle part(trackid, pdgid, primary);
408  part.AddTrajectoryPoint(pos, pvec);
409  mct.Add(part);
410  }// end All standard cases.
411  }//End Loop over all particles produces in this single decay.
412  }
413  }
414 
415  //Calculate an arbitrary direction with a given magnitude p
416  TLorentzVector RadioGen::dirCalc(double p, double m){
418  CLHEP::HepRandomEngine &engine = rng->getEngine();
419  CLHEP::RandFlat flat(engine);
420  // isotropic production angle for the decay product
421  double costheta = (2.0*flat.fire() - 1.0);
422  if (costheta < -1.0) costheta = -1.0;
423  if (costheta > 1.0) costheta = 1.0;
424  double sintheta = sqrt(1.0-costheta*costheta);
425  double phi = 2.0*M_PI*flat.fire();
426  TLorentzVector pvec(p*sintheta*std::cos(phi),
427  p*sintheta*std::sin(phi),
428  p*costheta,
429  std::sqrt(p*p+m*m));
430  return pvec;
431  }
432 
433 
434  // only reads those files that are on the fNuclide list. Copy information from the TGraphs to TH1D's
435 
436  void RadioGen::readfile(std::string nuclide, std::string filename)
437  {
438  int ifound = 0;
439  for (size_t i=0; i<fNuclide.size(); i++)
440  {
441  if (fNuclide[i] == nuclide){ //This check makes sure that the nuclide we are searching for is in fact in our fNuclide list. Ar42 handeled separately.
442  ifound = 1;
443  break;
444  } //End If nuclide is in our list. Next is the special case of Ar42
445  else if ( (std::regex_match(nuclide, std::regex( "42Ar.*" )) ) && fNuclide[i]=="42Ar" ){
446  ifound = 1;
447  break;
448  }
449  }
450  if (ifound == 0) return;
451 
452  Bool_t addStatus = TH1::AddDirectoryStatus();
453  TH1::AddDirectory(kFALSE); // cloned histograms go in memory, and aren't deleted when files are closed.
454  // be sure to restore this state when we're out of the routine.
455 
456 
457  spectrumname.push_back(nuclide);
458  cet::search_path sp("FW_SEARCH_PATH");
459  std::string fn2 = "Radionuclides/";
460  fn2 += filename;
461  std::string fullname;
462  sp.find_file(fn2, fullname);
463  struct stat sb;
464  if (fullname.empty() || stat(fullname.c_str(), &sb)!=0)
465  throw cet::exception("RadioGen") << "Input spectrum file "
466  << fn2
467  << " not found in FW_SEARCH_PATH!\n";
468 
469  TFile f(fullname.c_str(),"READ");
470  TGraph *alphagraph = (TGraph*) f.Get("Alphas");
471  TGraph *betagraph = (TGraph*) f.Get("Betas");
472  TGraph *gammagraph = (TGraph*) f.Get("Gammas");
473  TGraph *neutrongraph = (TGraph*) f.Get("Neutrons");
474 
475  if (alphagraph)
476  {
477  int np = alphagraph->GetN();
478  double *y = alphagraph->GetY();
479  std::string name;
480  name = "RadioGen_";
481  name += nuclide;
482  name += "_Alpha";
483  TH1D *alphahist = (TH1D*) new TH1D(name.c_str(),"Alpha Spectrum",np,0,np);
484  for (int i=0; i<np; i++)
485  {
486  alphahist->SetBinContent(i+1,y[i]);
487  alphahist->SetBinError(i+1,0);
488  }
489  alphaspectrum.push_back(alphahist);
490  alphaintegral.push_back(alphahist->Integral());
491  }
492  else
493  {
494  alphaspectrum.push_back(0);
495  alphaintegral.push_back(0);
496  }
497 
498 
499  if (betagraph)
500  {
501  int np = betagraph->GetN();
502 
503  double *y = betagraph->GetY();
504  std::string name;
505  name = "RadioGen_";
506  name += nuclide;
507  name += "_Beta";
508  TH1D *betahist = (TH1D*) new TH1D(name.c_str(),"Beta Spectrum",np,0,np);
509 
510  for (int i=0; i<np; i++)
511  {
512  betahist->SetBinContent(i+1,y[i]);
513  betahist->SetBinError(i+1,0);
514  }
515  betaspectrum.push_back(betahist);
516  betaintegral.push_back(betahist->Integral());
517  }
518  else
519  {
520  betaspectrum.push_back(0);
521  betaintegral.push_back(0);
522  }
523 
524  if (gammagraph)
525  {
526  int np = gammagraph->GetN();
527  double *y = gammagraph->GetY();
528  std::string name;
529  name = "RadioGen_";
530  name += nuclide;
531  name += "_Gamma";
532  TH1D *gammahist = (TH1D*) new TH1D(name.c_str(),"Gamma Spectrum",np,0,np);
533  for (int i=0; i<np; i++)
534  {
535  gammahist->SetBinContent(i+1,y[i]);
536  gammahist->SetBinError(i+1,0);
537  }
538  gammaspectrum.push_back(gammahist);
539  gammaintegral.push_back(gammahist->Integral());
540  }
541  else
542  {
543  gammaspectrum.push_back(0);
544  gammaintegral.push_back(0);
545  }
546 
547  if (neutrongraph)
548  {
549  int np = neutrongraph->GetN();
550  double *y = neutrongraph->GetY();
551  std::string name;
552  name = "RadioGen_";
553  name += nuclide;
554  name += "_Neutron";
555  TH1D *neutronhist = (TH1D*) new TH1D(name.c_str(),"Neutron Spectrum",np,0,np);
556  for (int i=0; i<np; i++)
557  {
558  neutronhist->SetBinContent(i+1,y[i]);
559  neutronhist->SetBinError(i+1,0);
560  }
561  neutronspectrum.push_back(neutronhist);
562  neutronintegral.push_back(neutronhist->Integral());
563  }
564  else
565  {
566  neutronspectrum.push_back(0);
567  neutronintegral.push_back(0);
568  }
569 
570  f.Close();
571  TH1::AddDirectory(addStatus);
572 
573  double total = alphaintegral.back() + betaintegral.back() + gammaintegral.back() + neutronintegral.back();
574  if (total>0)
575  {
576  alphaintegral.back() /= total;
577  betaintegral.back() /= total;
578  gammaintegral.back() /= total;
579  neutronintegral.back() /= total;
580  }
581  }
582 
583 
584  void RadioGen::samplespectrum(std::string nuclide, int &itype, double &t, double &m, double &p)
585  {
586 
588  CLHEP::HepRandomEngine &engine = rng->getEngine();
589  CLHEP::RandFlat flat(engine);
590 
591  int inuc = -1;
592  for (size_t i=0; i<spectrumname.size(); i++)
593  {
594  if (nuclide == spectrumname[i])
595  {
596  inuc = i;
597  break;
598  }
599  }
600  if (inuc == -1)
601  {
602  t=0; // throw an exception in the future
603  itype = 0;
604  throw cet::exception("RadioGen") << "Ununderstood nuclide: " << nuclide << "\n";
605  }
606 
607  double rtype = flat.fire();
608 
609  itype = -1;
610  m = 0;
611  p = 0;
612  for (int itry=0;itry<10;itry++) // maybe a tiny normalization issue with a sum of 0.99999999999 or something, so try a few times.
613  {
614  if (rtype <= alphaintegral[inuc] && alphaspectrum[inuc] != 0)
615  {
616  itype = 1000020040; // alpha
617  m = m_alpha;
618  t = samplefromth1d(alphaspectrum[inuc])/1000000.0;
619  }
620  else if (rtype <= alphaintegral[inuc]+betaintegral[inuc] && betaspectrum[inuc] != 0)
621  {
622  itype = 11; // beta
623  m = m_e;
624  t = samplefromth1d(betaspectrum[inuc])/1000000.0;
625  }
626  else if ( rtype <= alphaintegral[inuc] + betaintegral[inuc] + gammaintegral[inuc] && gammaspectrum[inuc] != 0)
627  {
628  itype = 22; // gamma
629  m = 0;
630  t = samplefromth1d(gammaspectrum[inuc])/1000000.0;
631  }
632  else if( neutronspectrum[inuc] != 0)
633  {
634  itype = 2112;
635  m = m_neutron;
636  t = samplefromth1d(neutronspectrum[inuc])/1000000.0;
637  }
638  if (itype >= 0) break;
639  }
640  if (itype == -1)
641  {
642  throw cet::exception("RadioGen") << "Normalization problem with nuclide: " << nuclide << "\n";
643  }
644  double e = t + m;
645  p = e*e - m*m;
646  if (p>=0)
647  { p = TMath::Sqrt(p); }
648  else
649  { p=0; }
650  }
651 
652  // this is just a copy of TH1::GetRandom that uses the art-managed CLHEP random number generator instead of gRandom
653  // and a better handling of negative bin contents
654 
655  double RadioGen::samplefromth1d(TH1D *hist)
656  {
658  CLHEP::HepRandomEngine &engine = rng->getEngine();
659  CLHEP::RandFlat flat(engine);
660 
661  int nbinsx = hist->GetNbinsX();
662  std::vector<double> partialsum;
663  partialsum.resize(nbinsx+1);
664  partialsum[0] = 0;
665 
666  for (int i=1;i<=nbinsx;i++)
667  {
668  double hc = hist->GetBinContent(i);
669  if ( hc < 0) throw cet::exception("RadioGen") << "Negative bin: " << i << " " << hist->GetName() << "\n";
670  partialsum[i] = partialsum[i-1] + hc;
671  }
672  double integral = partialsum[nbinsx];
673  if (integral == 0) return 0;
674  // normalize to unit sum
675  for (int i=1;i<=nbinsx;i++) partialsum[i] /= integral;
676 
677  double r1 = flat.fire();
678  int ibin = TMath::BinarySearch(nbinsx,&(partialsum[0]),r1);
679  Double_t x = hist->GetBinLowEdge(ibin+1);
680  if (r1 > partialsum[ibin]) x +=
681  hist->GetBinWidth(ibin+1)*(r1-partialsum[ibin])/(partialsum[ibin+1] - partialsum[ibin]);
682  return x;
683  }
684 
685 
686  //beta channel 1. No Gamma. beta Q value 3525.22 keV
687  //beta channel 2. 1 Gamma (1524.6 keV). beta Q value 2000.62
688  //beta channel 3. 1 Gamma Channel. 312.6 keV + gamma 2. beta Q value 1688.02 keV
689  //beta channel 4. 2 Gamma Channels. Either 899.7 keV (i 0.052) + gamma 2 or 2424.3 keV (i 0.020). beta Q value 1100.92 keV
690  //beta channel 5. 3 gamma channels. 692.0 keV + 1228.0 keV + Gamma 2 (i 0.0033) ||OR|| 1021.2 keV + gamma 4 (i 0.0201) ||OR|| 1920.8 keV + gamma 2 (i 0.041). beta Q value 79.82 keV
691  //No Ar42Gamma1 as beta channel 1 does not produce a dexcitation gamma.
692  void RadioGen::Ar42Gamma2(std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>>& v_prods){
693  ti_PDGID pdgid = 22; td_Mass m = 0.0; //we are writing gammas
694  std::vector<double> vd_p = {.0015246};//Momentum in GeV
695  for(auto p : vd_p){
696  std::tuple<ti_PDGID, td_Mass, TLorentzVector> prods = std::make_tuple(pdgid, m, dirCalc(p,m));
697  v_prods.push_back(prods);
698  }
699  }
700  void RadioGen::Ar42Gamma3(std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>>& v_prods){
701  ti_PDGID pdgid = 22; td_Mass m = 0.0; //we are writing gammas
702  std::vector<double> vd_p = {.0003126};
703  for(auto p : vd_p){
704  std::tuple<ti_PDGID, td_Mass, TLorentzVector> prods = std::make_tuple(pdgid, m, dirCalc(p,m));
705  v_prods.push_back(prods);
706  }
707  Ar42Gamma2(v_prods);
708  }
709  void RadioGen::Ar42Gamma4(std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>>& v_prods){
711  CLHEP::HepRandomEngine &engine = rng->getEngine();
712  CLHEP::RandFlat flat(engine);
713  ti_PDGID pdgid = 22; td_Mass m = 0.0; //we are writing gammas
714  double chan1 = (0.052 / (0.052+0.020) );
715  if(flat.fire()<chan1){
716  std::vector<double> vd_p = {.0008997};//Momentum in GeV
717  for(auto p : vd_p){
718  std::tuple<ti_PDGID, td_Mass, TLorentzVector> prods = std::make_tuple(pdgid, m, dirCalc(p,m));
719  v_prods.push_back(prods);
720  }
721  Ar42Gamma2(v_prods);
722  }else{
723  std::vector<double> vd_p = {.0024243};//Momentum in GeV
724  for(auto p : vd_p){
725  std::tuple<ti_PDGID, td_Mass, TLorentzVector> prods = std::make_tuple(pdgid, m, dirCalc(p,m));
726  v_prods.push_back(prods);
727  }
728  }
729  }
730  void RadioGen::Ar42Gamma5(std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>>& v_prods){
732  CLHEP::HepRandomEngine &engine = rng->getEngine();
733  CLHEP::RandFlat flat(engine);
734  ti_PDGID pdgid = 22; td_Mass m = 0.0; //we are writing gammas
735  double chan1 = ( 0.0033 / (0.0033 + 0.0201 + 0.041) ); double chan2 = ( 0.0201 / (0.0033 + 0.0201 + 0.041) );
736  double chanPick = flat.fire();
737  if(chanPick < chan1){
738  std::vector<double> vd_p = {0.000692, 0.001228};//Momentum in GeV
739  for(auto p : vd_p){
740  std::tuple<ti_PDGID, td_Mass, TLorentzVector> prods = std::make_tuple(pdgid, m, dirCalc(p,m));
741  v_prods.push_back(prods);
742  }
743  Ar42Gamma2(v_prods);
744  }else if (chanPick<(chan1+chan2)){
745  std::vector<double> vd_p = {0.0010212};//Momentum in GeV
746  for(auto p : vd_p){
747  std::tuple<ti_PDGID, td_Mass, TLorentzVector> prods = std::make_tuple(pdgid, m, dirCalc(p,m));
748  v_prods.push_back(prods);
749  }
750  Ar42Gamma4(v_prods);
751  }else{
752  std::vector<double> vd_p = {0.0019208};//Momentum in GeV
753  for(auto p : vd_p){
754  std::tuple<ti_PDGID, td_Mass, TLorentzVector> prods = std::make_tuple(pdgid, m, dirCalc(p,m));
755  v_prods.push_back(prods);
756  }
757  Ar42Gamma2(v_prods);
758  }
759  }
760 
761  // phase space generator for beta decay -- keep it as a comment in case we ever want to revive it
762 
763  // double RadioGen::betaphasespace(double mass, double q)
764  //{
765  // art::ServiceHandle<art::RandomNumberGenerator> rng;
766  // CLHEP::HepRandomEngine &engine = rng->getEngine();
767  // CLHEP::RandFlat flat(engine);
768  // double p = 0;
769  // double mi = mass+q+m_e;
770  // TLorentzVector p0(0,0,0,mi); // pre-decay four-vector
771  // double masses[3] = {0,m_e,mass}; // neutrino, electron, nucleus
772  // rg.SetDecay(p0,3,masses);
773  // double wmax = rg.GetWtMax();
774  // for (int igen=0;igen<1000;igen++) // cap the retries at 1000
775  // {
776  // double weight = rg.Generate(); // want to unweight these if possible
777  // TLorentzVector *e4v = rg.GetDecay(1); // get electron four-vector
778  // p = e4v->P();
779  // if (weight >= wmax * flat.fire()) break;
780  // }
781  //return p;
782  //}
783 
784 
785 
786 
787 }//end namespace evgen
788 
789 namespace evgen{
790 
792 
793 }//end namespace evgen
794 
795 #endif
796 
Float_t x
Definition: compare.C:6
void AddTrajectoryPoint(TLorentzVector const &position, TLorentzVector const &momentum)
Definition: MCParticle.h:261
std::vector< double > fZ0
Bottom corner z position (cm) in world coordinates.
void SetOrigin(simb::Origin_t origin)
Definition: MCTruth.h:78
std::vector< std::string > fNuclide
List of nuclides to simulate. Example: "39Ar".
Float_t y
Definition: compare.C:6
art::ProductID put(std::unique_ptr< PROD > &&)
Definition: Run.h:148
std::vector< TH1D * > alphaspectrum
Particle class.
void Add(simb::MCParticle &part)
Definition: MCTruth.h:77
TGeoManager * ROOTGeoManager() const
Access to the ROOT geometry description manager.
std::vector< double > neutronintegral
Definition: Run.h:30
std::vector< std::string > spectrumname
std::vector< TH1D * > neutronspectrum
TFile f
Definition: plotHisto.C:6
ProductID put(std::unique_ptr< PROD > &&product)
Definition: Event.h:102
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:265
std::vector< TH1D * > betaspectrum
base_engine_t & getEngine() const
std::vector< double > fT1
End of time window to simulate in ns.
std::vector< double > fY1
Top corner y position (cm) in world coordinates.
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:42
std::string DetectorName() const
Returns a string with the name of the detector, as configured.
std::vector< double > fZ1
Top corner z position (cm) in world coordinates.
single particles thrown at the detector
Definition: MCTruth.h:24
T get(std::string const &key) const
Definition: ParameterSet.h:231
TString part[npart]
Definition: Style.C:32
int trackidcounter
Serial number for the MC track ID.
double energy
Definition: plottest35.C:25
An art service to assist in the distribution of guaranteed unique seeds to all engines within an art ...
Framework includes.
std::vector< double > alphaintegral
std::vector< double > betaintegral
TH2F * hist
Definition: plot.C:136
std::vector< double > fT0
Beginning of time window to simulate in ns.
#define LOG_DEBUG(id)
std::vector< std::string > fMaterial
List of regexes of materials in which to generate the decays. Example: "LAr".
std::vector< double > fX0
Bottom corner x position (cm) in world coordinates.
Event generator information.
Definition: MCTruth.h:30
Float_t e
Definition: plot.C:34
std::vector< double > gammaintegral
std::vector< double > fBq
Radioactivity in Becquerels (decay per sec) per cubic cm.
Namespace collecting geometry-related classes utilities.
Event Generation using GENIE, cosmics or single particles.
std::vector< double > fX1
Top corner x position (cm) in world coordinates.
art framework interface to geometry description
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
std::vector< TH1D * > gammaspectrum
std::vector< double > fY0
Bottom corner y position (cm) in world coordinates.