RadioGen_module.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 /// \file RadioGen_module.cc
3 /// \brief Generator for radiological decays
4 ///
5 /// Module designed to produce a set list of particles for MC to model radiological decays
6 ///
7 /// \version $Id: RadioGen_module.cc,v 1.0 2014/09/05 15:02:00 trj Exp $
8 /// \author trj@fnal.gov
9 // Rn222 generation feature added by gleb.sinev@duke.edu
10 // (based on a generator by jason.stock@mines.sdsmt.edu)
11 ////////////////////////////////////////////////////////////////////////
12 #ifndef EVGEN_RADIOLOGICAL
13 #define EVGEN_RADIOLOGICAL
14 
15 // C++ includes.
16 #include <iostream>
17 #include <sstream>
18 #include <string>
19 #include <cmath>
20 #include <memory>
21 #include <iterator>
22 #include <vector>
23 #include <sys/stat.h>
24 
25 // Framework includes
28 #include "fhiclcpp/ParameterSet.h"
31 #include "art_root_io/TFileService.h"
32 #include "art_root_io/TFileDirectory.h"
36 #include "cetlib_except/exception.h"
37 #include "cetlib/search_path.h"
38 
39 // art extensions
40 
41 // nutools includes
44 #include "nugen/EventGeneratorBase/evgenbase.h"
45 #include "nurandom/RandomUtils/NuRandomService.h"
46 
47 // gar includes
48 #include "Geometry/GeometryGAr.h"
50 #include "CoreUtils/ServiceUtil.h"
51 
52 // root includes
53 
54 #include "TLorentzVector.h"
55 #include "TVector3.h"
56 #include "TGenPhaseSpace.h"
57 #include "TMath.h"
58 #include "TFile.h"
59 #include "TH1.h"
60 #include "TH1D.h"
61 #include "TGraph.h"
62 
63 #include "CLHEP/Random/RandFlat.h"
64 #include "CLHEP/Random/RandPoisson.h"
65 
66 namespace simb { class MCTruth; }
67 
68 namespace gar {
69  namespace evgen {
70 
71  /// Module to generate particles created by radiological decay, patterend off of SingleGen
72  /// Currently it generates only in rectangular prisms oriented along the x,y,z axes
73 
74  class RadioGen : public ::art::EDProducer {
75 
76  public:
77  explicit RadioGen(fhicl::ParameterSet const& pset);
78  virtual ~RadioGen();
79 
80  // This is called for each event.
81  void produce(::art::Event& evt);
82  void beginRun(::art::Run& run);
83  void reconfigure(fhicl::ParameterSet const& p);
84 
85  private:
86 
87  void SampleOne(unsigned int i,
88  simb::MCTruth &mct);
89 
90  void readfile(std::string nuclide, std::string filename);
91  void samplespectrum(std::string nuclide, int &itype, double &t, double &m, double &p);
92 
93  // recoded so as to use the GArSoft-managed random number generator
94  double samplefromth1d(TH1D *hist);
95 
96  // itype = pdg code: 1000020040: alpha. itype=11: beta. -11: positron, itype=22: gamma. -1: error
97  // t is the kinetic energy in GeV (= E-m)
98  // m = mass in GeV
99  // p = momentum in GeV
100 
101  //double betaphasespace(double mass, double q); // older parameterization.
102 
103  std::vector<std::string> fNuclide; ///< List of nuclides to simulate. Example: "39Ar".
104  std::vector<double> fBq; ///< Radioactivity in Becquerels (decay per sec) per cubic cm.
105  std::vector<double> fT0; ///< Beginning of time window to simulate in ns
106  std::vector<double> fT1; ///< End of time window to simulate in ns
107  std::vector<double> fX0; ///< Bottom corner x position (cm) in world coordinates
108  std::vector<double> fY0; ///< Bottom corner y position (cm) in world coordinates
109  std::vector<double> fZ0; ///< Bottom corner z position (cm) in world coordinates
110  std::vector<double> fX1; ///< Top corner x position (cm) in world coordinates
111  std::vector<double> fY1; ///< Top corner y position (cm) in world coordinates
112  std::vector<double> fZ1; ///< Top corner z position (cm) in world coordinates
113  int trackidcounter; ///< Serial number for the MC track ID
114 
115  // leftovers from the phase space generator
116  // const double gevperamu = 0.931494061;
117  // TGenPhaseSpace rg; // put this here so we don't constantly construct and destruct it
118 
119  const double m_e = 0.000510998928; // mass of electron in GeV
120  const double m_alpha = 3.727379240; // mass of an alpha particle in GeV
121 
122  std::vector<std::string> spectrumname;
123  std::vector<TH1D*> alphaspectrum;
124  std::vector<double> alphaintegral;
125  std::vector<TH1D*> betaspectrum;
126  std::vector<double> betaintegral;
127  std::vector<TH1D*> gammaspectrum;
128  std::vector<double> gammaintegral;
129 
130  CLHEP::HepRandomEngine &fEngine;
131 
132  };
133  }
134 
135  namespace evgen{
136 
137  //____________________________________________________________________________
138  RadioGen::RadioGen(fhicl::ParameterSet const& pset) : EDProducer{pset},
140  {
141 
142  this->reconfigure(pset);
143 
144  produces< std::vector<simb::MCTruth> >();
145  produces< sumdata::RunData, ::art::InRun >();
146 
147  }
148 
149  //____________________________________________________________________________
151  {
152  }
153 
154  //____________________________________________________________________________
156  {
157  // do not put seed in reconfigure because we don't want to reset
158  // the seed midstream -- same as SingleGen
159 
160  fNuclide = p.get< std::vector<std::string>>("Nuclide");
161  fBq = p.get< std::vector<double> >("BqPercc");
162  fT0 = p.get< std::vector<double> >("T0");
163  fT1 = p.get< std::vector<double> >("T1");
164  fX0 = p.get< std::vector<double> >("X0");
165  fY0 = p.get< std::vector<double> >("Y0");
166  fZ0 = p.get< std::vector<double> >("Z0");
167  fX1 = p.get< std::vector<double> >("X1");
168  fY1 = p.get< std::vector<double> >("Y1");
169  fZ1 = p.get< std::vector<double> >("Z1");
170  // check for consistency of vector sizes
171 
172  unsigned int nsize = fNuclide.size();
173  if ( fBq.size() != nsize ) throw cet::exception("RadioGen") << "Different size Bq vector and Nuclide vector\n";
174  if ( fT0.size() != nsize ) throw cet::exception("RadioGen") << "Different size T0 vector and Nuclide vector\n";
175  if ( fT1.size() != nsize ) throw cet::exception("RadioGen") << "Different size T1 vector and Nuclide vector\n";
176  if ( fX0.size() != nsize ) throw cet::exception("RadioGen") << "Different size X0 vector and Nuclide vector\n";
177  if ( fY0.size() != nsize ) throw cet::exception("RadioGen") << "Different size Y0 vector and Nuclide vector\n";
178  if ( fZ0.size() != nsize ) throw cet::exception("RadioGen") << "Different size Z0 vector and Nuclide vector\n";
179  if ( fX1.size() != nsize ) throw cet::exception("RadioGen") << "Different size X1 vector and Nuclide vector\n";
180  if ( fY1.size() != nsize ) throw cet::exception("RadioGen") << "Different size Y1 vector and Nuclide vector\n";
181  if ( fZ1.size() != nsize ) throw cet::exception("RadioGen") << "Different size Z1 vector and Nuclide vector\n";
182 
183  readfile("39Ar","Argon_39.root");
184  readfile("60Co","Cobalt_60.root");
185  readfile("85Kr","Krypton_85.root");
186  readfile("40K","Potassium_40.root");
187  readfile("232Th","Thorium_232.root");
188  readfile("238U","Uranium_238.root");
189 
190  return;
191  }
192 
193  //____________________________________________________________________________
195  {
196 
197  // grab the geometry object to see what geometry we are using
198 
199  auto geo = gar::providerFrom<geo::GeometryGAr>();
200  std::unique_ptr<sumdata::RunData> runcol(new sumdata::RunData(geo->DetectorName()));
201  run.put(std::move(runcol));
202 
203  return;
204  }
205 
206  //____________________________________________________________________________
208  {
209 
210  ///unique_ptr allows ownership to be transferred to the ::art::Event after the put statement
211  std::unique_ptr< std::vector<simb::MCTruth> > truthcol(new std::vector<simb::MCTruth>);
212 
213  simb::MCTruth truth;
215 
216  trackidcounter = -1;
217  for (unsigned int i=0; i<fNuclide.size(); ++i) {
218  SampleOne(i,truth);
219  }//end loop over nuclides
220 
221  MF_LOG_DEBUG("RadioGen") << truth;
222  truthcol->push_back(truth);
223  evt.put(std::move(truthcol));
224  return;
225  }
226 
227  //____________________________________________________________________________
228  // Generate radioactive decays per nuclide per volume according to the FCL parameters
229  void RadioGen::SampleOne(unsigned int i, simb::MCTruth &mct){
230 
231  // get the random number generator service and make some CLHEP generators
232  CLHEP::RandFlat flat(fEngine);
233  //CLHEP::RandGauss gauss(fEngine);
234  CLHEP::RandPoisson poisson(fEngine);
235 
236  // figure out how many decays to generate
237 
238  double rate = fabs( fBq[i] * (fT1[i] - fT0[i]) * (fX1[i] - fX0[i]) * (fY1[i] - fY0[i]) * (fZ1[i] - fZ0[i]) ) / 1.0E9;
239  long ndecays = poisson.shoot(rate);
240 
241  for (unsigned int idecay=0; idecay<ndecays; idecay++)
242  {
243  // generate just one particle at a time. Need a little recoding if a radioactive
244  // decay generates multiple daughters that need simulation
245 
246  int pdgid=0; // electron=11, photon=22, alpha = 1000020040
247  double t = 0; // kinetic energy of particle GeV
248  double m = 0; // mass of daughter particle GeV
249  double p = 0; // generated momentum (GeV)
250 
251  // Treat 222Rn separately
252  if (fNuclide[i] == "222Rn")
253  {
254  pdgid = 1000020040;
255  t = 0.00548952;
256  m = m_alpha;
257  double energy = t + m;
258  double p2 = energy*energy - m*m;
259  if (p2 > 0) p = TMath::Sqrt(p2);
260  else p = 0;
261  }
262  else samplespectrum(fNuclide[i],pdgid,t,m,p);
263 
264 
265  // uniformly distributed in position and time
266 
267  TLorentzVector pos( fX0[i] + flat.fire()*(fX1[i] - fX0[i]),
268  fY0[i] + flat.fire()*(fY1[i] - fY0[i]),
269  fZ0[i] + flat.fire()*(fZ1[i] - fZ0[i]),
270  fT0[i] + flat.fire()*(fT1[i] - fT0[i]) );
271 
272  // isotropic production angle for the decay product
273 
274  double costheta = (2.0*flat.fire() - 1.0);
275  if (costheta < -1.0) costheta = -1.0;
276  if (costheta > 1.0) costheta = 1.0;
277  double sintheta = sqrt(1.0-costheta*costheta);
278  double phi = 2.0*M_PI*flat.fire();
279 
280  TLorentzVector pvec(p*sintheta*std::cos(phi),
281  p*sintheta*std::sin(phi),
282  p*costheta,
283  std::sqrt(p*p+m*m));
284 
285  // set track id to a negative serial number as these are all primary particles and have id <= 0
286  int trackid = trackidcounter;
287  trackidcounter--;
288  std::string primary("primary");
289  // alpha particles need a little help since they're not in the TDatabasePDG table
290  // so don't rely so heavily on default arguments to the MCParticle constructor
291  if (pdgid == 1000020040)
292  {
293  simb::MCParticle part(trackid, pdgid, primary,-1,m,1);
294  part.AddTrajectoryPoint(pos, pvec);
295  mct.Add(part);
296  }
297  else
298  {
299  simb::MCParticle part(trackid, pdgid, primary);
300  part.AddTrajectoryPoint(pos, pvec);
301  mct.Add(part);
302  }
303  }
304  }
305 
306  //--------------------------------------------------------------------------
307  // only reads those files that are on the fNuclide list. Copy information
308  // from the TGraphs to TH1D's
310  {
311  int ifound = 0;
312  for (size_t i=0; i<fNuclide.size(); i++)
313  {
314  if (fNuclide[i] == nuclide)
315  {
316  ifound = 1;
317  break;
318  }
319  }
320  if (ifound == 0) return;
321 
322  Bool_t addStatus = TH1::AddDirectoryStatus();
323  TH1::AddDirectory(kFALSE); // cloned histograms go in memory, and aren't deleted when files are closed.
324  // be sure to restore this state when we're out of the routine.
325 
326 
327  spectrumname.push_back(nuclide);
328 
329  cet::search_path sp("FW_SEARCH_PATH");
330  std::string fn2 = "Radionuclides/";
331  fn2 += filename;
332  std::string fullname;
333  sp.find_file(fn2, fullname);
334  struct stat sb;
335  if (fullname.empty() || stat(fullname.c_str(), &sb)!=0)
336  throw cet::exception("RadioGen") << "Input spectrum file "
337  << fn2
338  << " not found in FW_SEARCH_PATH!\n";
339 
340  TFile f(fullname.c_str(),"READ");
341  TGraph *alphagraph = (TGraph*) f.Get("Alphas");
342  TGraph *betagraph = (TGraph*) f.Get("Betas");
343  TGraph *gammagraph = (TGraph*) f.Get("Gammas");
344 
345  if (alphagraph)
346  {
347  int np = alphagraph->GetN();
348  double *y = alphagraph->GetY();
350  name = "RadioGen_";
351  name += nuclide;
352  name += "_Alpha";
353  TH1D *alphahist = (TH1D*) new TH1D(name.c_str(),"Alpha Spectrum",np,0,np-1);
354  for (int i=0; i<np; i++)
355  {
356  alphahist->SetBinContent(i+1,y[i]);
357  alphahist->SetBinError(i+1,0);
358  }
359  alphaspectrum.push_back(alphahist);
360  alphaintegral.push_back(alphahist->Integral());
361  }
362  else
363  {
364  alphaspectrum.push_back(0);
365  alphaintegral.push_back(0);
366  }
367 
368 
369  if (betagraph)
370  {
371  int np = betagraph->GetN();
372 
373  double *y = betagraph->GetY();
375  name = "RadioGen_";
376  name += nuclide;
377  name += "_Beta";
378  TH1D *betahist = (TH1D*) new TH1D(name.c_str(),"Beta Spectrum",np,0,np-1);
379 
380  for (int i=0; i<np; i++)
381  {
382  betahist->SetBinContent(i+1,y[i]);
383  betahist->SetBinError(i+1,0);
384  }
385  betaspectrum.push_back(betahist);
386  betaintegral.push_back(betahist->Integral());
387  }
388  else
389  {
390  betaspectrum.push_back(0);
391  betaintegral.push_back(0);
392  }
393 
394  if (gammagraph)
395  {
396  int np = gammagraph->GetN();
397  double *y = gammagraph->GetY();
399  name = "RadioGen_";
400  name += nuclide;
401  name += "_Gamma";
402  TH1D *gammahist = (TH1D*) new TH1D(name.c_str(),"Gamma Spectrum",np,0,np-1);
403  for (int i=0; i<np; i++)
404  {
405  gammahist->SetBinContent(i+1,y[i]);
406  gammahist->SetBinError(i+1,0);
407  }
408  gammaspectrum.push_back(gammahist);
409  gammaintegral.push_back(gammahist->Integral());
410  }
411  else
412  {
413  gammaspectrum.push_back(0);
414  gammaintegral.push_back(0);
415  }
416 
417  f.Close();
418  TH1::AddDirectory(addStatus);
419 
420  double total = alphaintegral.back() + betaintegral.back() + gammaintegral.back();
421  if (total>0)
422  {
423  alphaintegral.back() /= total;
424  betaintegral.back() /= total;
425  gammaintegral.back() /= total;
426  }
427  }
428 
429  //--------------------------------------------------------------------------
430  void RadioGen::samplespectrum(std::string nuclide, int &itype, double &t, double &m, double &p)
431  {
432 
433  CLHEP::RandFlat flat(fEngine);
434 
435  int inuc = -1;
436  for (size_t i=0; i<spectrumname.size(); i++)
437  {
438  if (nuclide == spectrumname[i])
439  {
440  inuc = i;
441  break;
442  }
443  }
444  if (inuc == -1)
445  {
446  t=0; // throw an exception in the future
447  itype = 0;
448  throw cet::exception("RadioGen") << "Ununderstood nuclide: " << nuclide << "\n";
449  }
450 
451  double rtype = flat.fire();
452 
453  itype = -1;
454  m = 0;
455  p = 0;
456  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.
457  {
458  if (rtype <= alphaintegral[inuc] && alphaspectrum[inuc] != 0)
459  {
460  itype = 1000020040; // alpha
461  m = m_alpha;
462  t = samplefromth1d(alphaspectrum[inuc])/1000000.0;
463  }
464  else if (rtype <= alphaintegral[inuc]+betaintegral[inuc] && betaspectrum[inuc] != 0)
465  {
466  itype = 11; // beta
467  m = m_e;
468  t = samplefromth1d(betaspectrum[inuc])/1000000.0;
469  }
470  else if ( gammaspectrum[inuc] != 0)
471  {
472  itype = 22; // gamma
473  m = 0;
474  t = samplefromth1d(gammaspectrum[inuc])/1000000.0;
475  }
476  if (itype >= 0) break;
477  }
478  if (itype == -1)
479  {
480  throw cet::exception("RadioGen") << "Normalization problem with nuclide: " << nuclide << "\n";
481  }
482  double e = t + m;
483  p = e*e - m*m;
484  if (p>=0)
485  { p = TMath::Sqrt(p); }
486  else
487  { p=0; }
488  }
489 
490  // this is just a copy of TH1::GetRandom that uses the art-managed
491  //CLHEP random number generator instead of gRandom
492  // and a better handling of negative bin contents
494  {
495  CLHEP::RandFlat flat(fEngine);
496 
497  int nbinsx = hist->GetNbinsX();
498  std::vector<double> partialsum;
499  partialsum.resize(nbinsx+1);
500  partialsum[0] = 0;
501 
502  for (int i=1;i<=nbinsx;i++)
503  {
504  double hc = hist->GetBinContent(i);
505  if ( hc < 0) throw cet::exception("RadioGen") << "Negative bin: " << i << " " << hist->GetName() << "\n";
506  partialsum[i] = partialsum[i-1] + hc;
507  }
508  double integral = partialsum[nbinsx];
509  if (integral == 0) return 0;
510  // normalize to unit sum
511  for (int i=1;i<=nbinsx;i++) partialsum[i] /= integral;
512 
513  double r1 = flat.fire();
514  int ibin = TMath::BinarySearch(nbinsx,&(partialsum[0]),r1);
515  Double_t x = hist->GetBinLowEdge(ibin+1);
516  if (r1 > partialsum[ibin]) x +=
517  hist->GetBinWidth(ibin+1)*(r1-partialsum[ibin])/(partialsum[ibin+1] - partialsum[ibin]);
518  return x;
519  }
520 
521  }//end namespace evgen
522 
523  namespace evgen{
524 
526 
527  }//end namespace evgen
528 } // namespace gar
529 
530 #endif
531 ////////////////////////////////////////////////////////////////////////
static QCString name
Definition: declinfo.cpp:673
base_engine_t & createEngine(seed_t seed)
std::vector< double > fX0
Bottom corner x position (cm) in world coordinates.
std::vector< TH1D * > gammaspectrum
void readfile(std::string nuclide, std::string filename)
std::vector< double > fY1
Top corner y position (cm) in world coordinates.
std::vector< double > fT1
End of time window to simulate in ns.
void SampleOne(unsigned int i, simb::MCTruth &mct)
void AddTrajectoryPoint(TLorentzVector const &position, TLorentzVector const &momentum)
Definition: MCParticle.h:257
void SetOrigin(simb::Origin_t origin)
Definition: MCTruth.h:82
std::string string
Definition: nybbler.cc:12
std::vector< double > fZ1
Top corner z position (cm) in world coordinates.
Particle class.
std::vector< double > alphaintegral
string filename
Definition: train.py:213
std::vector< double > fT0
Beginning of time window to simulate in ns.
Definition: Run.h:17
std::vector< double > fX1
Top corner x position (cm) in world coordinates.
std::vector< std::string > fNuclide
List of nuclides to simulate. Example: "39Ar".
std::vector< double > fBq
Radioactivity in Becquerels (decay per sec) per cubic cm.
const double e
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:67
virtual void reconfigure(fhicl::ParameterSet const &pset)
single particles thrown at the detector
Definition: MCTruth.h:26
def move(depos, offset)
Definition: depos.py:107
T get(std::string const &key) const
Definition: ParameterSet.h:271
std::vector< TH1D * > alphaspectrum
p
Definition: test.py:223
std::vector< double > fY0
Bottom corner y position (cm) in world coordinates.
ProductID put(std::unique_ptr< PROD > &&edp, std::string const &instance={})
Definition: DataViewImpl.h:686
double samplefromth1d(TH1D *hist)
std::vector< double > fZ0
Bottom corner z position (cm) in world coordinates.
std::vector< double > gammaintegral
Base utilities and modules for event generation and detector simulation.
void reconfigure(fhicl::ParameterSet const &p)
#define M_PI
Definition: includeROOT.h:54
CLHEP::HepRandomEngine & fEngine
void beginRun(::art::Run &run)
General GArSoft Utilities.
void Add(simb::MCParticle const &part)
Definition: MCTruth.h:80
void samplespectrum(std::string nuclide, int &itype, double &t, double &m, double &p)
std::vector< TH1D * > betaspectrum
#define MF_LOG_DEBUG(id)
std::string find_file(std::string const &filename) const
Definition: search_path.cc:96
list x
Definition: train.py:276
std::vector< std::string > spectrumname
TCEvent evt
Definition: DataStructs.cxx:7
Event generator information.
Definition: MCTruth.h:32
std::vector< double > betaintegral
int trackidcounter
Serial number for the MC track ID.
LArSoft geometry interface.
Definition: ChannelGeo.h:16
art framework interface to geometry description
Event Generation using GENIE, cosmics or single particles.
nbinsx
New: trying to make a variation.
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
void produce(::art::Event &evt)