All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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 /// \author trj@fnal.gov
8 // 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.
30 ////////////////////////////////////////////////////////////////////////
31 
32 // C++ includes.
33 #include <cassert>
34 #include <cmath>
35 #include <iterator>
36 #include <memory>
37 #include <regex>
38 #include <string>
39 #include <sys/stat.h>
40 #include <utility> // std::pair<>, std::tuple<>
41 #include <vector>
42 
43 // Framework includes
44 #include "art/Framework/Core/EDProducer.h"
45 #include "art/Framework/Principal/Event.h"
46 #include "art/Framework/Principal/Run.h"
47 #include "fhiclcpp/ParameterSet.h"
48 #include "art/Framework/Principal/Handle.h"
49 #include "art/Framework/Services/Registry/ServiceHandle.h"
50 #include "art/Framework/Core/ModuleMacros.h"
51 #include "messagefacility/MessageLogger/MessageLogger.h"
52 #include "cetlib_except/exception.h"
53 #include "cetlib/search_path.h"
54 #include "cetlib/exempt_ptr.h"
55 
56 // nurandom includes
57 #include "nurandom/RandomUtils/NuRandomService.h"
58 
59 // nusimdata, nugen includes
60 #include "nusimdata/SimulationBase/MCTruth.h"
61 #include "nusimdata/SimulationBase/MCParticle.h"
62 #include "nugen/EventGeneratorBase/evgenbase.h"
63 
64 // lar includes
77 #include "larcorealg/Geometry/geo_vectors_utils.h" // geo::...::makeFromCoords()
82 
83 // root includes
84 
85 #include "TFile.h"
86 #include "TGenPhaseSpace.h"
87 #include "TGeoBBox.h"
88 #include "TGeoManager.h"
89 #include "TGeoMaterial.h"
90 #include "TGeoNode.h"
91 #include "TGeoVolume.h"
92 #include "TGraph.h"
93 #include "TH1.h"
94 #include "TH1D.h"
95 #include "TLorentzVector.h"
96 #include "TMath.h"
97 
98 #include "CLHEP/Random/RandFlat.h"
99 #include "CLHEP/Random/RandPoisson.h"
100 
101 namespace simb { class MCTruth; }
102 
103 namespace evgen {
104 
105  /**
106  * @brief Module to generate particles created by radiological decay,
107  * patterned off of `SingleGen`.
108  *
109  * The module generates the products of radioactive decay of some known
110  * nuclides.
111  * Each nuclide decay producing a single particle (with exceptions, as for
112  * example argon(A=42)), whose spectrum is specified in a ROOT file to be
113  * found in the `FW_SEARCH_PATH` path, and it can be a alpha, beta, gamma or
114  * neutron. In case multiple decay channels are possible, each decay is
115  * stochastically chosen weighting the channels according to the integral of
116  * their spectrum. The normalization of the spectrum is otherwise ignored.
117  *
118  * Nuclides can be added by making the proper distributions available in
119  * a file called after the name used for it in the `Nuclide` configuration
120  * parameter (e.g `14C.root` if the nuclide key is `14C`): check the existing
121  * nuclide files for examples.
122  *
123  * A special treatment is encoded for argon(A=42) and radon(A=222) (and,
124  * "temporary", for nichel(A=59) ).
125  *
126  * Decays happen only in volumes specified in the configuration, and with a
127  * rate also specified in the configuration. Volumes are always box-shaped,
128  * and can be specified by coordinates or by name. In addition, within each
129  * volume decays will be generated only in the subvolumes matching the
130  * specified materials.
131  *
132  *
133  * @note All beta decays emit positrons.
134  *
135  *
136  * Configuration parameters
137  * -------------------------
138  *
139  * * `X0`, `Y0`, `Z0`, `X1`, `Y1`, `Z1` (lists of real numbers, optional):
140  * if specified, they describe the box volumes where to generate decays;
141  * all lists must have the same size, and each entry `i` defines the box
142  * between coordinates `(X0[i], Y0[i], Z0[i])` and `(X1[i], Y1[i], Z1[i])`
143  * expressed in world coordinates and in centimeters;
144  * * `Volumes` (list of strings, optional): if specified, each entry
145  * represents all the volumes in the geometry whose name exactly matches
146  * the entry; the volumes are added _after_ the ones explicitly listed by
147  * their coordinates (configuration parameters `X0`, `Y0`, `Z0`, `X1`,
148  * `Y1` and `Z1`); each volume name counts as one entry, even if it
149  * expands to multiple volumes;
150  * `Volumes` can be omitted if volumes are specified with the coordinate
151  * parameters;
152  * * `Nuclide` (list of strings, mandatory): the list of decaying nuclides,
153  * one per volume entry; note that each of the elements in `X0` (and
154  * the other 5 coordinate parameters) and each of the elements in
155  * `Volumes` counts as one entry, even for entries of the latter which
156  * match multiple volumes (in that case, all matching volumes are
157  * assigned the same nuclide parameters);
158  * since documentation is never maintained, refer to the code for a list
159  * of the supported materials; a subset of them is:
160  * `39Ar`, `60Co`, `85Kr`, `40K`, `232Th`, `238U`, `222Rn`, `59Ni` and
161  * `42Ar`;
162  * * `Material` (list of regular expressions, mandatory): for each nuclide,
163  * the name of the materials allowed to decay in this mode; this name
164  * is a regular expression (C++ default `std::regex`), which needs to
165  * match the name of the material as specified in the detector geometry
166  * (usually in GDML format); a material is mandatory for each nuclide;
167  * if no material selection is desired, the all-matching pattern `".*"`
168  * can be used;
169  * * `BqPercc` (list of real numbers, mandatory): activity of the nuclides, in
170  * the same order as they are in `Nuclide` parameter; each value is
171  * specified as becquerel per cubic centimeter;
172  * * `T0`, `T1` (list of real values, optional): range of time when the decay
173  * may happen, in @ref DetectorClocksSimulationTime "simulation time";
174  * each nuclide is assigned a time range; differently from the other
175  * parameters, the list of times _can_ be shorter than the number of
176  * nuclides, in which case all the nuclides with no matching time range
177  * will be assigned the last range specified; as a specially special case,
178  * if no time is specified at all, the time window is assigned as from
179  * one readout window
180  * (`detinfo::DetectorPropertiesData::ReadOutWindowSize()`) before the
181  * @ref DetectorClocksHardwareTrigger "nominal hardware trigger time"
182  * (`detinfo::DetectorClocksData::TriggerTime()`) up to a number of
183  * ticks after the trigger time equivalent to the full simulated TPC
184  * waveform (`detinfo::DetectorPropertiesData::NumberTimeSamples()`);
185  * this makes it a quite poor default, so you may want to avoid it.
186  *
187  */
188  class RadioGen : public art::EDProducer {
189  public:
190  explicit RadioGen(fhicl::ParameterSet const& pset);
191 
192  private:
193  // This is called for each event.
194  void produce(art::Event& evt);
195  void beginRun(art::Run& run);
196 
197  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
198  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
199 
200  /// Adds all volumes with the specified name to the coordinates.
201  /// Volumes must be boxes aligned to the world frame axes.
202  std::size_t addvolume(std::string const& volumeName);
203 
204  /// Checks that the node represents a box well aligned to world frame axes.
205  void SampleOne(unsigned int i,
206  simb::MCTruth &mct);
207 
208  TLorentzVector dirCalc(double p, double m);
209 
210  void readfile(std::string nuclide, std::string const& filename);
211  void samplespectrum(std::string nuclide, int &itype, double &t, double &m, double &p);
212 
213  void Ar42Gamma2(std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>>& v_prods);
214  void Ar42Gamma3(std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>>& v_prods);
215  void Ar42Gamma4(std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>>& v_prods);
216  void Ar42Gamma5(std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>>& v_prods);
217 
218  // recoded so as to use the LArSoft-managed random number generator
219  double samplefromth1d(TH1D& hist);
220 
221  /// Prints the settings for the specified nuclide and volume.
222  template <typename Stream>
224  (Stream&& out, std::size_t iNucl, std::string const& volumeName = {})
225  const;
226 
227 
228  /// Returns the start and end of the readout window.
229  std::pair<double, double> defaulttimewindow() const;
230 
231  // itype = pdg code: 1000020040: alpha. itype=11: beta. -11: positron, itype=22: gamma. -1: error
232  // t is the kinetic energy in GeV (= E-m)
233  // m = mass in GeV
234  // p = momentum in GeV
235 
236  //double betaphasespace(double mass, double q); // older parameterization.
237 
238  // the generator randomly samples points in a rectangular prism of space and time, and only selects those points in
239  // volumes with materials that match the regexes in fMaterial. One can use wildcards * and ? for broader matches.
240 
241  std::vector<std::string> fNuclide; ///< List of nuclides to simulate. Example: "39Ar".
242  std::vector<std::string> fMaterial; ///< List of regexes of materials in which to generate the decays. Example: "LAr"
243  std::vector<double> fBq; ///< Radioactivity in Becquerels (decay per sec) per cubic cm.
244  std::vector<double> fT0; ///< Beginning of time window to simulate in ns
245  std::vector<double> fT1; ///< End of time window to simulate in ns
246  std::vector<double> fX0; ///< Bottom corner x position (cm) in world coordinates
247  std::vector<double> fY0; ///< Bottom corner y position (cm) in world coordinates
248  std::vector<double> fZ0; ///< Bottom corner z position (cm) in world coordinates
249  std::vector<double> fX1; ///< Top corner x position (cm) in world coordinates
250  std::vector<double> fY1; ///< Top corner y position (cm) in world coordinates
251  std::vector<double> fZ1; ///< Top corner z position (cm) in world coordinates
253  int trackidcounter; ///< Serial number for the MC track ID
254 
255 
256  // leftovers from the phase space generator
257  // const double gevperamu = 0.931494061;
258  // TGenPhaseSpace rg; // put this here so we don't constantly construct and destruct it
259 
260  std::vector<std::string> spectrumname;
261  std::vector<std::unique_ptr<TH1D>> alphaspectrum;
262  std::vector<double> alphaintegral;
263  std::vector<std::unique_ptr<TH1D>> betaspectrum;
264  std::vector<double> betaintegral;
265  std::vector<std::unique_ptr<TH1D>> gammaspectrum;
266  std::vector<double> gammaintegral;
267  std::vector<std::unique_ptr<TH1D>> neutronspectrum;
268  std::vector<double> neutronintegral;
269  CLHEP::HepRandomEngine& fEngine;
270  };
271 }
272 
273 namespace {
274  constexpr double m_e = 0.000510998928; // mass of electron in GeV
275  constexpr double m_alpha = 3.727379240; // mass of an alpha particle in GeV
276  constexpr double m_neutron = 0.9395654133; // mass of a neutron in GeV
277 }
278 
279 
280 namespace evgen{
281 
282  //____________________________________________________________________________
283  RadioGen::RadioGen(fhicl::ParameterSet const& pset)
284  : EDProducer{pset}
285  , fX0{pset.get< std::vector<double> >("X0", {})}
286  , fY0{pset.get< std::vector<double> >("Y0", {})}
287  , fZ0{pset.get< std::vector<double> >("Z0", {})}
288  , fX1{pset.get< std::vector<double> >("X1", {})}
289  , fY1{pset.get< std::vector<double> >("Y1", {})}
290  , fZ1{pset.get< std::vector<double> >("Z1", {})}
291  , fIsFirstSignalSpecial{pset.get< bool >("IsFirstSignalSpecial", false)}
292  // create a default random engine; obtain the random seed from NuRandomService,
293  // unless overridden in configuration with key "Seed"
294  , fEngine(art::ServiceHandle<rndm::NuRandomService>{}->createEngine(*this, pset, "Seed"))
295  {
296  produces< std::vector<simb::MCTruth> >();
297  produces< sumdata::RunData, art::InRun >();
298 
299 
300  auto const nuclide = pset.get< std::vector<std::string>>("Nuclide");
301  auto const material = pset.get< std::vector<std::string>>("Material");
302  auto const Bq = pset.get< std::vector<double> >("BqPercc");
303  auto t0 = pset.get< std::vector<double> >("T0", {});
304  auto t1 = pset.get< std::vector<double> >("T1", {});
305 
306  if (fT0.empty() || fT1.empty()) { // better be both empty...
307  if (!fT0.empty() || !fT1.empty()) {
308  throw art::Exception(art::errors::Configuration)
309  << "RadioGen T0 and T1 need to be both non-empty, or both empty"
310  " (now T0 has " << fT0.size() << " entries and T1 has " << fT0.size()
311  << ")\n";
312  }
313  auto const [ defaultT0, defaultT1 ] = defaulttimewindow();
314  t0.push_back(defaultT0);
315  t1.push_back(defaultT1);
316  }
317 
318  //
319  // First, the materials are assigned to the coordinates explicitly
320  // configured; then, each of the remaining materials is assigned to one
321  // of the named volumes.
322  // TODO: reaction to mismatches is "not so great".
323  //
324  mf::LogInfo("RadioGen") << "Configuring activity:";
325  for (std::size_t iCoord: util::counter(fX0.size())) {
326 
327  fNuclide.push_back(nuclide.at(iCoord));
328  fMaterial.push_back(material.at(iCoord));
329  fBq.push_back(Bq.at(iCoord));
330  // replicate the last timing if none is specified
331  fT0.push_back(t0[std::min(iCoord, t0.size() - 1U)]);
332  fT1.push_back(t1[std::min(iCoord, t1.size() - 1U)]);
333 
334  dumpNuclideSettings(mf::LogVerbatim("RadioGen"), iCoord);
335 
336  } // for
337 
338  std::size_t iNext = fX0.size();
339  auto const volumes = pset.get<std::vector<std::string>>("Volumes", {});
340  for (auto&& [ iVolume, volName ]: util::enumerate(volumes)) {
341  // this expands the coordinate vectors
342  auto const nVolumes = addvolume(volName);
343  if (nVolumes == 0) {
344  throw art::Exception(art::errors::Configuration)
345  << "No volume named '" << volName << "' was found!\n";
346  }
347 
348  std::size_t const iVolFirst = fNuclide.size();
349  for (auto iVolInstance: util::counter(nVolumes)) {
350  fNuclide.push_back(nuclide.at(iNext));
351  fMaterial.push_back(material.at(iNext));
352  fBq.push_back(Bq.at(iNext));
353  // replicate the last timing if none is specified
354  fT0.push_back(t0[std::min(iNext, t0.size() - 1U)]);
355  fT1.push_back(t1[std::min(iNext, t1.size() - 1U)]);
356 
357  dumpNuclideSettings
358  (mf::LogVerbatim("RadioGen"), iVolFirst + iVolInstance, volName);
359 
360  }
361  ++iNext;
362  } // for
363 
364  // check for consistency of vector sizes
365  unsigned int nsize = fNuclide.size();
366  if (nsize == 0) {
367  throw art::Exception(art::errors::Configuration)
368  << "No nuclide configured!\n";
369  }
370 
371  if ( fMaterial.size() != nsize ) throw cet::exception("RadioGen") << "Different size Material vector and Nuclide vector\n";
372  if ( fBq.size() != nsize ) throw cet::exception("RadioGen") << "Different size Bq vector and Nuclide vector\n";
373  if ( fT0.size() != nsize ) throw cet::exception("RadioGen") << "Different size T0 vector and Nuclide vector\n";
374  if ( fT1.size() != nsize ) throw cet::exception("RadioGen") << "Different size T1 vector and Nuclide vector\n";
375  if ( fX0.size() != nsize ) throw cet::exception("RadioGen") << "Different size X0 vector and Nuclide vector\n";
376  if ( fY0.size() != nsize ) throw cet::exception("RadioGen") << "Different size Y0 vector and Nuclide vector\n";
377  if ( fZ0.size() != nsize ) throw cet::exception("RadioGen") << "Different size Z0 vector and Nuclide vector\n";
378  if ( fX1.size() != nsize ) throw cet::exception("RadioGen") << "Different size X1 vector and Nuclide vector\n";
379  if ( fY1.size() != nsize ) throw cet::exception("RadioGen") << "Different size Y1 vector and Nuclide vector\n";
380  if ( fZ1.size() != nsize ) throw cet::exception("RadioGen") << "Different size Z1 vector and Nuclide vector\n";
381 
382  for(std::string & nuclideName : fNuclide){
383  if(nuclideName=="39Ar" ){readfile("39Ar","Argon_39.root") ;}
384  else if(nuclideName=="60Co" ){readfile("60Co","Cobalt_60.root") ;}
385  else if(nuclideName=="85Kr" ){readfile("85Kr","Krypton_85.root") ;}
386  else if(nuclideName=="40K" ){readfile("40K","Potassium_40.root") ;}
387  else if(nuclideName=="232Th"){readfile("232Th","Thorium_232.root");}
388  else if(nuclideName=="238U" ){readfile("238U","Uranium_238.root") ;}
389  else if(nuclideName=="222Rn"){continue;} //Rn222 is handled separately later
390  else if(nuclideName=="59Ni"){continue;} //Rn222 is handled separately later
391  else if(nuclideName=="42Ar" ){
392  readfile("42Ar_1", "Argon_42_1.root"); //Each possible beta decay mode of Ar42 is given it's own .root file for now.
393  readfile("42Ar_2", "Argon_42_2.root"); //This allows us to know which decay chain to follow for the dexcitation gammas.
394  readfile("42Ar_3", "Argon_42_3.root"); //The dexcitation gammas are not included in the root files as we want to
395  readfile("42Ar_4", "Argon_42_4.root"); //probabilistically simulate the correct coincident gammas, which we cannot guarantee
396  readfile("42Ar_5", "Argon_42_5.root"); //by sampling a histogram.
397  continue;
398  } //Ar42 is handeled separately later
399  else{
400  std::string searchName = nuclideName;
401  searchName+=".root";
402  readfile(nuclideName,searchName);
403  }
404  }
405  }
406 
407  //____________________________________________________________________________
408  void RadioGen::beginRun(art::Run& run)
409  {
410  art::ServiceHandle<geo::Geometry const> geo;
411  run.put(std::make_unique<sumdata::RunData>(geo->DetectorName()));
412  }
413 
414  //____________________________________________________________________________
415  void RadioGen::produce(art::Event& evt)
416  {
417  ///unique_ptr allows ownership to be transferred to the art::Event after the put statement
418  std::unique_ptr< std::vector<simb::MCTruth> > truthcol(new std::vector<simb::MCTruth>);
419 
420  simb::MCTruth truth;
421  truth.SetOrigin(simb::kSingleParticle);
422 
423  trackidcounter = -1;
424  for (unsigned int i=0; i<fNuclide.size(); ++i) {
425  SampleOne(i,truth);
426  }//end loop over nuclides
427 
428  MF_LOG_DEBUG("RadioGen") << truth;
429  truthcol->push_back(truth);
430  evt.put(std::move(truthcol));
431  }
432 
433  //____________________________________________________________________________
434  std::size_t RadioGen::addvolume(std::string const& volumeName) {
435 
436  auto const& geom = *(lar::providerFrom<geo::Geometry>());
437 
438  std::vector<geo::GeoNodePath> volumePaths;
439  auto findVolume = [&volumePaths, volumeName](auto& path)
440  {
441  if (path.current().GetVolume()->GetName() == volumeName)
442  volumePaths.push_back(path);
443  return true;
444  };
445 
446  geo::ROOTGeometryNavigator navigator { *(geom.ROOTGeoManager()) };
447 
448  navigator.apply(findVolume);
449 
450  for (geo::GeoNodePath const& path: volumePaths) {
451 
452  //
453  // find the coordinates of the volume in local coordinates
454  //
455  TGeoShape const* pShape = path.current().GetVolume()->GetShape();
456  auto pBox = dynamic_cast<TGeoBBox const*>(pShape);
457  if (!pBox) {
458  throw cet::exception("RadioGen") << "Volume '"
459  << path.current().GetName() << "' is a " << pShape->IsA()->GetName()
460  << ", not a TGeoBBox.\n";
461  }
462 
463  geo::Point_t const origin
464  = geo::vect::makeFromCoords<geo::Point_t>(pBox->GetOrigin());
465  geo::Vector_t const diag
466  = { pBox->GetDX(), pBox->GetDY(), pBox->GetDZ() };
467 
468  //
469  // convert to world coordinates
470  //
471 
472  auto const trans
473  = path.currentTransformation<geo::TransformationMatrix>();
474 
475  geo::Point_t min, max;
476  trans.Transform(origin - diag, min);
477  trans.Transform(origin + diag, max);
478 
479  //
480  // add to the coordinates
481  //
482  fX0.push_back(std::min(min.X(), max.X()));
483  fY0.push_back(std::min(min.Y(), max.Y()));
484  fZ0.push_back(std::min(min.Z(), max.Z()));
485  fX1.push_back(std::max(min.X(), max.X()));
486  fY1.push_back(std::max(min.Y(), max.Y()));
487  fZ1.push_back(std::max(min.Z(), max.Z()));
488 
489  } // for
490 
491  return volumePaths.size();
492  } // RadioGen::addvolume()
493 
494 
495  //____________________________________________________________________________
496  // Generate radioactive decays per nuclide per volume according to the FCL parameters
497 
498  void RadioGen::SampleOne(unsigned int i, simb::MCTruth &mct)
499  {
500  art::ServiceHandle<geo::Geometry const> geo;
501  TGeoManager *geomanager = geo->ROOTGeoManager();
502 
503  CLHEP::RandFlat flat(fEngine);
504  CLHEP::RandPoisson poisson(fEngine);
505 
506  // figure out how many decays to generate, assuming that the entire prism consists of the radioactive material.
507  // we will skip over decays in other materials later.
508 
509  double rate = fabs( fBq[i] * (fT1[i] - fT0[i]) * (fX1[i] - fX0[i]) * (fY1[i] - fY0[i]) * (fZ1[i] - fZ0[i]) ) / 1.0E9;
510  long ndecays = poisson.shoot(rate);
511 
512  std::regex const re_material{fMaterial[i]};
513  for (unsigned int idecay=0; idecay<ndecays; idecay++)
514  {
515  // generate just one particle at a time. Need a little recoding if a radioactive
516  // decay generates multiple daughters that need simulation
517  // uniformly distributed in position and time
518  //
519  // JStock: Leaving this as a single position for the decay products. For now I will assume they all come from the same spot.
520  TLorentzVector pos( fX0[i] + flat.fire()*(fX1[i] - fX0[i]),
521  fY0[i] + flat.fire()*(fY1[i] - fY0[i]),
522  fZ0[i] + flat.fire()*(fZ1[i] - fZ0[i]),
523  (idecay==0 && fIsFirstSignalSpecial) ? 0 : ( fT0[i] + flat.fire()*(fT1[i] - fT0[i]) ) );
524 
525  // discard decays that are not in the proper material
526  std::string volmaterial = geomanager->FindNode(pos.X(),pos.Y(),pos.Z())->GetMedium()->GetMaterial()->GetName();
527  if (!std::regex_match(volmaterial, re_material)) continue;
528 
529  //Moved pdgid into the next statement, so that it is localized.
530  // electron=11, photon=22, alpha = 1000020040, neutron = 2112
531 
532  //JStock: Allow us to have different particles from the same decay. This requires multiple momenta.
533  std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>> v_prods; //(First is for PDGID, second is mass, third is Momentum)
534 
535  if (fNuclide[i] == "222Rn") // Treat 222Rn separately
536  {
537  double p=0; double t=0.00548952; td_Mass m=m_alpha; ti_PDGID pdgid=1000020040; //td_Mass = double. ti_PDGID = int;
538  double energy = t + m;
539  double p2 = energy*energy - m*m;
540  if (p2 > 0) p = TMath::Sqrt(p2);
541  else p = 0;
542  v_prods.emplace_back(pdgid, m, dirCalc(p, m));
543  }//End special case RN222
544  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.
545  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.
546  v_prods.emplace_back(pdgid, m, dirCalc(p,m));
547  }//end special case Ni59 calibration source
548  else if(fNuclide[i] == "42Ar"){ // Spot for special treatment of Ar42.
549  double p=0; double t=0; td_Mass m = 0; ti_PDGID pdgid=0; //td_Mass = double. ti_PDGID = int;
550  double bSelect = flat.fire(); //Make this a random number from 0 to 1.
551  if(bSelect<0.819){ //beta channel 1. No Gamma. beta Q value 3525.22 keV
552  samplespectrum("42Ar_1", pdgid, t, m, p);
553  v_prods.emplace_back(pdgid, m, dirCalc(p, m));
554  //No gamma here.
555  }else if(bSelect<0.9954){ //beta channel 2. 1 Gamma (1524.6 keV). beta Q value 2000.62
556  samplespectrum("42Ar_2", pdgid, t, m, p);
557  v_prods.emplace_back(pdgid, m, dirCalc(p, m));
558  Ar42Gamma2(v_prods);
559  }else if(bSelect<0.9988){ //beta channel 3. 1 Gamma Channel. 312.6 keV + gamma 2. beta Q value 1688.02 keV
560  samplespectrum("42Ar_3", pdgid, t, m, p);
561  v_prods.emplace_back(pdgid, m, dirCalc(p, m));
562  Ar42Gamma3(v_prods);
563  }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
564  samplespectrum("42Ar_4", pdgid, t, m, p);
565  v_prods.emplace_back(pdgid, m, dirCalc(p, m));
566  Ar42Gamma4(v_prods);
567  }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
568  samplespectrum("42Ar_5", pdgid, t, m, p);
569  v_prods.emplace_back(pdgid, m, dirCalc(p, m));
570  Ar42Gamma5(v_prods);
571  }
572  //Add beta.
573  //Call gamma function for beta mode.
574  }
575  else{ //General Case.
576  double p=0; double t=0; td_Mass m = 0; ti_PDGID pdgid=0; //td_Mass = double. ti_PDGID = int;
577  samplespectrum(fNuclide[i],pdgid,t,m,p);
578  std::tuple<ti_PDGID, td_Mass, TLorentzVector> partMassMom = std::make_tuple(pdgid, m, dirCalc(p,m));
579  v_prods.push_back(partMassMom);
580  }//end else (not RN or other special case
581 
582  //JStock: Modify this to now loop over the v_prods.
583  for(auto prodEntry : v_prods){
584  // set track id to a negative serial number as these are all primary particles and have id <= 0
585  int trackid = trackidcounter;
586  ti_PDGID pdgid = std::get<0>(prodEntry);
587  td_Mass m = std::get<1>(prodEntry);
588  TLorentzVector pvec = std::get<2>(prodEntry);
589  trackidcounter--;
590  std::string primary("primary");
591 
592  // alpha particles need a little help since they're not in the TDatabasePDG table
593  // // so don't rely so heavily on default arguments to the MCParticle constructor
594  if (pdgid == 1000020040){
595  simb::MCParticle part(trackid, pdgid, primary,-1,m,1);
596  part.AddTrajectoryPoint(pos, pvec);
597  mct.Add(part);
598  }// end "If alpha"
599  else{
600  simb::MCParticle part(trackid, pdgid, primary);
601  part.AddTrajectoryPoint(pos, pvec);
602  mct.Add(part);
603  }// end All standard cases.
604  }//End Loop over all particles produces in this single decay.
605  }
606  }
607 
608  //Calculate an arbitrary direction with a given magnitude p
609  TLorentzVector RadioGen::dirCalc(double p, double m)
610  {
611  CLHEP::RandFlat flat(fEngine);
612  // isotropic production angle for the decay product
613  double costheta = (2.0*flat.fire() - 1.0);
614  if (costheta < -1.0) costheta = -1.0;
615  if (costheta > 1.0) costheta = 1.0;
616  double const sintheta = sqrt(1.0-costheta*costheta);
617  double const phi = 2.0*M_PI*flat.fire();
618  return TLorentzVector{p*sintheta*std::cos(phi),
619  p*sintheta*std::sin(phi),
620  p*costheta,
621  std::sqrt(p*p+m*m)};
622  }
623 
624  // only reads those files that are on the fNuclide list. Copy information from the TGraphs to TH1D's
625 
626  void RadioGen::readfile(std::string nuclide, std::string const& filename)
627  {
628  bool found{false};
629  std::regex const re_argon{"42Ar.*"};
630  for (size_t i=0; i<fNuclide.size(); i++)
631  {
632  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.
633  found = true;
634  break;
635  } //End If nuclide is in our list. Next is the special case of Ar42
636  else if (std::regex_match(nuclide, re_argon) && fNuclide[i]=="42Ar") {
637  found = true;
638  break;
639  }
640  }
641  if (!found) return;
642 
643  Bool_t addStatus = TH1::AddDirectoryStatus();
644  TH1::AddDirectory(kFALSE); // cloned histograms go in memory, and aren't deleted when files are closed.
645  // be sure to restore this state when we're out of the routine.
646 
647  spectrumname.push_back(nuclide);
648  cet::search_path sp("FW_SEARCH_PATH");
649  std::string fn2 = "Radionuclides/";
650  fn2 += filename;
651  std::string fullname;
652  sp.find_file(fn2, fullname);
653  struct stat sb;
654  if (fullname.empty() || stat(fullname.c_str(), &sb)!=0)
655  throw cet::exception("RadioGen") << "Input spectrum file "
656  << fn2
657  << " not found in FW_SEARCH_PATH!\n";
658 
659  TFile f(fullname.c_str(),"READ");
660  TGraph *alphagraph = (TGraph*) f.Get("Alphas");
661  TGraph *betagraph = (TGraph*) f.Get("Betas");
662  TGraph *gammagraph = (TGraph*) f.Get("Gammas");
663  TGraph *neutrongraph = (TGraph*) f.Get("Neutrons");
664 
665  if (alphagraph)
666  {
667  int np = alphagraph->GetN();
668  double *y = alphagraph->GetY();
669  std::string name;
670  name = "RadioGen_";
671  name += nuclide;
672  name += "_Alpha";
673  auto alphahist = std::make_unique<TH1D>(name.c_str(),"Alpha Spectrum",np,0,np);
674  for (int i=0; i<np; i++) {
675  alphahist->SetBinContent(i+1,y[i]);
676  alphahist->SetBinError(i+1,0);
677  }
678  alphaintegral.push_back(alphahist->Integral());
679  alphaspectrum.push_back(move(alphahist));
680  }
681  else
682  {
683  alphaintegral.push_back(0);
684  alphaspectrum.push_back(nullptr);
685  }
686 
687 
688  if (betagraph)
689  {
690  int np = betagraph->GetN();
691 
692  double *y = betagraph->GetY();
693  std::string name;
694  name = "RadioGen_";
695  name += nuclide;
696  name += "_Beta";
697  auto betahist = std::make_unique<TH1D>(name.c_str(),"Beta Spectrum",np,0,np);
698  for (int i=0; i<np; i++) {
699  betahist->SetBinContent(i+1,y[i]);
700  betahist->SetBinError(i+1,0);
701  }
702  betaintegral.push_back(betahist->Integral());
703  betaspectrum.push_back(move(betahist));
704  }
705  else
706  {
707  betaintegral.push_back(0);
708  betaspectrum.push_back(nullptr);
709  }
710 
711  if (gammagraph)
712  {
713  int np = gammagraph->GetN();
714  double *y = gammagraph->GetY();
715  std::string name;
716  name = "RadioGen_";
717  name += nuclide;
718  name += "_Gamma";
719  auto gammahist = std::make_unique<TH1D>(name.c_str(),"Gamma Spectrum",np,0,np);
720  for (int i=0; i<np; i++) {
721  gammahist->SetBinContent(i+1,y[i]);
722  gammahist->SetBinError(i+1,0);
723  }
724  gammaintegral.push_back(gammahist->Integral());
725  gammaspectrum.push_back(move(gammahist));
726  }
727  else
728  {
729  gammaintegral.push_back(0);
730  gammaspectrum.push_back(nullptr);
731  }
732 
733  if (neutrongraph)
734  {
735  int np = neutrongraph->GetN();
736  double *y = neutrongraph->GetY();
737  std::string name;
738  name = "RadioGen_";
739  name += nuclide;
740  name += "_Neutron";
741  auto neutronhist = std::make_unique<TH1D>(name.c_str(),"Neutron Spectrum",np,0,np);
742  for (int i=0; i<np; i++)
743  {
744  neutronhist->SetBinContent(i+1,y[i]);
745  neutronhist->SetBinError(i+1,0);
746  }
747  neutronintegral.push_back(neutronhist->Integral());
748  neutronspectrum.push_back(move(neutronhist));
749  }
750  else
751  {
752  neutronintegral.push_back(0);
753  neutronspectrum.push_back(nullptr);
754  }
755 
756  f.Close();
757  TH1::AddDirectory(addStatus);
758 
759  double total = alphaintegral.back() + betaintegral.back() + gammaintegral.back() + neutronintegral.back();
760  if (total>0)
761  {
762  alphaintegral.back() /= total;
763  betaintegral.back() /= total;
764  gammaintegral.back() /= total;
765  neutronintegral.back() /= total;
766  }
767  }
768 
769 
770  void RadioGen::samplespectrum(std::string nuclide, int &itype, double &t, double &m, double &p)
771  {
772  CLHEP::RandFlat flat(fEngine);
773 
774  int inuc = -1;
775  for (size_t i=0; i<spectrumname.size(); i++)
776  {
777  if (nuclide == spectrumname[i])
778  {
779  inuc = i;
780  break;
781  }
782  }
783  if (inuc == -1)
784  {
785  t=0; // throw an exception in the future
786  itype = 0;
787  throw cet::exception("RadioGen") << "Ununderstood nuclide: " << nuclide << "\n";
788  }
789 
790  double rtype = flat.fire();
791 
792  itype = -1;
793  m = 0;
794  p = 0;
795  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.
796  {
797  if (rtype <= alphaintegral[inuc] && alphaspectrum[inuc] != nullptr)
798  {
799  itype = 1000020040; // alpha
800  m = m_alpha;
801  t = samplefromth1d(*alphaspectrum[inuc])/1000000.0;
802  }
803  else if (rtype <= alphaintegral[inuc]+betaintegral[inuc] && betaspectrum[inuc] != nullptr)
804  {
805  itype = 11; // beta
806  m = m_e;
807  t = samplefromth1d(*betaspectrum[inuc])/1000000.0;
808  }
809  else if ( rtype <= alphaintegral[inuc] + betaintegral[inuc] + gammaintegral[inuc] && gammaspectrum[inuc] != nullptr)
810  {
811  itype = 22; // gamma
812  m = 0;
813  t = samplefromth1d(*gammaspectrum[inuc])/1000000.0;
814  }
815  else if( neutronspectrum[inuc] != nullptr)
816  {
817  itype = 2112;
818  m = m_neutron;
819  t = samplefromth1d(*neutronspectrum[inuc])/1000000.0;
820  }
821  if (itype >= 0) break;
822  }
823  if (itype == -1)
824  {
825  throw cet::exception("RadioGen") << "Normalization problem with nuclide: " << nuclide << "\n";
826  }
827  double e = t + m;
828  p = e*e - m*m;
829  if (p>=0)
830  { p = TMath::Sqrt(p); }
831  else
832  { p=0; }
833  }
834 
835  // this is just a copy of TH1::GetRandom that uses the art-managed CLHEP random number generator instead of gRandom
836  // and a better handling of negative bin contents
837 
838  double RadioGen::samplefromth1d(TH1D& hist)
839  {
840  CLHEP::RandFlat flat(fEngine);
841 
842  int nbinsx = hist.GetNbinsX();
843  std::vector<double> partialsum;
844  partialsum.resize(nbinsx+1);
845  partialsum[0] = 0;
846 
847  for (int i=1;i<=nbinsx;i++)
848  {
849  double hc = hist.GetBinContent(i);
850  if ( hc < 0) throw cet::exception("RadioGen") << "Negative bin: " << i << " " << hist.GetName() << "\n";
851  partialsum[i] = partialsum[i-1] + hc;
852  }
853  double integral = partialsum[nbinsx];
854  if (integral == 0) return 0;
855  // normalize to unit sum
856  for (int i=1;i<=nbinsx;i++) partialsum[i] /= integral;
857 
858  double r1 = flat.fire();
859  int ibin = TMath::BinarySearch(nbinsx,&(partialsum[0]),r1);
860  Double_t x = hist.GetBinLowEdge(ibin+1);
861  if (r1 > partialsum[ibin]) {
862  x += hist.GetBinWidth(ibin+1)*(r1-partialsum[ibin])/(partialsum[ibin+1] - partialsum[ibin]);
863  }
864  return x;
865  }
866 
867 
868  //Ar42 uses BNL tables for K-42 from Aug 2017
869  //beta channel 1. No Gamma. beta Q value 3525.22 keV
870  //beta channel 2. 1 Gamma (1524.6 keV). beta Q value 2000.62
871  //beta channel 3. 1 Gamma Channel. 312.6 keV + gamma 2. beta Q value 1688.02 keV
872  //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
873  //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
874  //No Ar42Gamma1 as beta channel 1 does not produce a dexcitation gamma.
875  void RadioGen::Ar42Gamma2(std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>>& v_prods)
876  {
877  ti_PDGID pdgid = 22; td_Mass m = 0.0; //we are writing gammas
878  std::vector<double> vd_p = {.0015246};//Momentum in GeV
879  for(auto p : vd_p){
880  v_prods.emplace_back(pdgid, m, dirCalc(p, m));
881  }
882  }
883 
884  void RadioGen::Ar42Gamma3(std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>>& v_prods)
885  {
886  ti_PDGID pdgid = 22; td_Mass m = 0.0; //we are writing gammas
887  std::vector<double> vd_p = {.0003126};
888  for(auto p : vd_p){
889  v_prods.emplace_back(pdgid, m, dirCalc(p, m));
890  }
891  Ar42Gamma2(v_prods);
892  }
893 
894  void RadioGen::Ar42Gamma4(std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>>& v_prods)
895  {
896  CLHEP::RandFlat flat(fEngine);
897  ti_PDGID pdgid = 22; td_Mass m = 0.0; //we are writing gammas
898  double chan1 = (0.052 / (0.052+0.020) );
899  if(flat.fire()<chan1){
900  std::vector<double> vd_p = {.0008997};//Momentum in GeV
901  for(auto p : vd_p){
902  v_prods.emplace_back(pdgid, m, dirCalc(p, m));
903  }
904  Ar42Gamma2(v_prods);
905  }else{
906  std::vector<double> vd_p = {.0024243};//Momentum in GeV
907  for(auto p : vd_p){
908  v_prods.emplace_back(pdgid, m, dirCalc(p, m));
909  }
910  }
911  }
912 
913  void RadioGen::Ar42Gamma5(std::vector<std::tuple<ti_PDGID, td_Mass, TLorentzVector>>& v_prods)
914  {
915  CLHEP::RandFlat flat(fEngine);
916  ti_PDGID pdgid = 22; td_Mass m = 0.0; //we are writing gammas
917  double chan1 = ( 0.0033 / (0.0033 + 0.0201 + 0.041) ); double chan2 = ( 0.0201 / (0.0033 + 0.0201 + 0.041) );
918  double chanPick = flat.fire();
919  if(chanPick < chan1){
920  std::vector<double> vd_p = {0.000692, 0.001228};//Momentum in GeV
921  for(auto p : vd_p){
922  v_prods.emplace_back(pdgid, m, dirCalc(p, m));
923  }
924  Ar42Gamma2(v_prods);
925  }else if (chanPick<(chan1+chan2)){
926  std::vector<double> vd_p = {0.0010212};//Momentum in GeV
927  for(auto p : vd_p){
928  v_prods.emplace_back(pdgid, m, dirCalc(p, m));
929  }
930  Ar42Gamma4(v_prods);
931  }else{
932  std::vector<double> vd_p = {0.0019208};//Momentum in GeV
933  for(auto p : vd_p){
934  v_prods.emplace_back(pdgid, m, dirCalc(p, m));
935  }
936  Ar42Gamma2(v_prods);
937  }
938  }
939 
940  // phase space generator for beta decay -- keep it as a comment in case we ever want to revive it
941 
942  // double RadioGen::betaphasespace(double mass, double q)
943  //{
944  // CLHEP::RandFlat flat(fEngine);
945  // double p = 0;
946  // double mi = mass+q+m_e;
947  // TLorentzVector p0(0,0,0,mi); // pre-decay four-vector
948  // double masses[3] = {0,m_e,mass}; // neutrino, electron, nucleus
949  // rg.SetDecay(p0,3,masses);
950  // double wmax = rg.GetWtMax();
951  // for (int igen=0;igen<1000;igen++) // cap the retries at 1000
952  // {
953  // double weight = rg.Generate(); // want to unweight these if possible
954  // TLorentzVector *e4v = rg.GetDecay(1); // get electron four-vector
955  // p = e4v->P();
956  // if (weight >= wmax * flat.fire()) break;
957  // }
958  //return p;
959  //}
960 
961 
962  //____________________________________________________________________________
963  template <typename Stream>
965  (Stream&& out, std::size_t iNucl, std::string const& volumeName /* = {} */)
966  const
967  {
968 
969  out << "[#" << iNucl << "] "
970  << fNuclide[iNucl]
971  << " (" << fBq[iNucl] << " Bq/cm^3)"
972  << " in " << fMaterial[iNucl]
973  << " from " << fT0[iNucl] << " to " << fT1[iNucl] << " ns in ( "
974  << fX0[iNucl] << ", " << fY0[iNucl] << ", " << fZ0[iNucl] << ") to ( "
975  << fX1[iNucl] << ", " << fY1[iNucl] << ", " << fZ1[iNucl] << ")";
976  if (!volumeName.empty()) out << " (\"" << volumeName << "\")";
977 
978  } // RadioGen::dumpNuclideSettings()
979 
980 
981  //____________________________________________________________________________
982  std::pair<double, double> RadioGen::defaulttimewindow() const {
983  // values are in simulation time scale (and therefore in [ns])
984  using namespace detinfo::timescales;
985 
986  auto const& timings = detinfo::makeDetectorTimings
987  (art::ServiceHandle<detinfo::DetectorClocksService>()->DataForJob());
988  detinfo::DetectorPropertiesData const& detInfo
989  = art::ServiceHandle<detinfo::DetectorPropertiesService>()->DataForJob();
990 
991  //
992  // we take a number of (TPC electronics) ticks before the trigger time,
993  // and we go to a number of ticks after the trigger time;
994  // that shift is one readout window size
995  //
996 
997  auto const trigTimeTick
998  = timings.toTick<electronics_tick>(timings.TriggerTime());
999  electronics_time_ticks const beforeTicks
1000  { -static_cast<int>(detInfo.ReadOutWindowSize()) };
1001  electronics_time_ticks const afterTicks { detInfo.NumberTimeSamples() };
1002 
1003  return {
1004  double(timings.toTimeScale<simulation_time>(trigTimeTick + beforeTicks)),
1005  double(timings.toTimeScale<simulation_time>(trigTimeTick + afterTicks))
1006  };
1007  } // RadioGen::defaulttimewindow()
1008 
1009 }//end namespace evgen
1010 
1011 DEFINE_ART_MODULE(evgen::RadioGen)
std::vector< std::unique_ptr< TH1D > > gammaspectrum
ROOT::Math::DisplacementVector3D< ROOT::Math::Cartesian3D< double >, ROOT::Math::GlobalCoordinateSystemTag > Vector_t
Type for representation of momenta in 3D space.
Definition: geo_vectors.h:164
TLorentzVector dirCalc(double p, double m)
Utilities related to art service access.
timescale_traits< ElectronicsTimeCategory >::tick_t electronics_tick
A point on the electronics time scale expressed in its ticks.
bool apply(geo::GeoNodePath &path, Op &&op) const
Applies the specified operation to all nodes under the path.
process_name opflash particleana ie x
createEngine fT0
std::vector< double > fZ0
Bottom corner z position (cm) in world coordinates.
Executes an operation on all the nodes of the ROOT geometry.
Definition of util::enumerate().
std::vector< std::string > fNuclide
List of nuclides to simulate. Example: &quot;39Ar&quot;.
std::pair< double, double > defaulttimewindow() const
Returns the start and end of the readout window.
BEGIN_PROLOG could also be dds filename
pdgs p
Definition: selectors.fcl:22
Class representing a path in ROOT geometry.
Class representing a path in ROOT geometry.
void Ar42Gamma5(std::vector< std::tuple< ti_PDGID, td_Mass, TLorentzVector >> &v_prods)
void dumpNuclideSettings(Stream &&out, std::size_t iNucl, std::string const &volumeName={}) const
Prints the settings for the specified nuclide and volume.
void readfile(std::string nuclide, std::string const &filename)
tuple m
now if test mode generate materials, CRT shell, world, gdml header else just generate CRT shell for u...
std::vector< double > neutronintegral
auto enumerate(Iterables &&...iterables)
Range-for loop helper tracking the number of iteration.
Definition: enumerate.h:69
std::vector< std::string > spectrumname
Interface to detinfo::DetectorClocks.
BEGIN_PROLOG triggeremu_data_config_icarus settings PMTADCthresholds sequence::icarus_stage0_multiTPC_TPC physics sequence::icarus_stage0_EastHits_TPC physics sequence::icarus_stage0_WestHits_TPC physics producers purityana0 caloskimCalorimetryCryoE physics caloskimCalorimetryCryoW physics path
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:265
Access the description of detector geometry.
std::vector< std::unique_ptr< TH1D > > alphaspectrum
std::vector< double > fT1
End of time window to simulate in ns.
auto counter(T begin, T end)
Returns an object to iterate values from begin to end in a range-for loop.
Definition: counter.h:285
std::vector< double > fY1
Top corner y position (cm) in world coordinates.
process_name opflash particleana ie ie y
timescale_traits< ElectronicsTimeCategory >::tick_interval_t electronics_time_ticks
An interval on the electronics time scale expressed in its ticks.
Definitions of geometry vector data types.
timescale_traits< SimulationTimeCategory >::time_point_t simulation_time
A point in time on the simulation time scale.
std::vector< double > fZ1
Top corner z position (cm) in world coordinates.
detinfo::DetectorTimings makeDetectorTimings(detinfo::DetectorClocksData const &detClocks)
int trackidcounter
Serial number for the MC track ID.
fEngine(art::ServiceHandle< rndm::NuRandomService >() ->createEngine(*this, pset,"Seed"))
Utilities to extend the interface of geometry vectors.
void SampleOne(unsigned int i, simb::MCTruth &mct)
Checks that the node represents a box well aligned to world frame axes.
void Ar42Gamma3(std::vector< std::tuple< ti_PDGID, td_Mass, TLorentzVector >> &v_prods)
Test of util::counter and support utilities.
void beginRun(art::Run &run)
Module to generate particles created by radiological decay, patterned off of SingleGen.
double samplefromth1d(TH1D &hist)
std::vector< double > alphaintegral
std::size_t addvolume(std::string const &volumeName)
void samplespectrum(std::string nuclide, int &itype, double &t, double &m, double &p)
std::vector< double > betaintegral
void Ar42Gamma2(std::vector< std::tuple< ti_PDGID, td_Mass, TLorentzVector >> &v_prods)
Selection of the type of transformation matrix used in geometry.
Var Sqrt(const Var &v)
Use to take sqrt of a var.
Definition: Var.cxx:172
CLHEP::HepRandomEngine & fEngine
std::vector< double > fT0
Beginning of time window to simulate in ns.
do i e
then echo fcl name
Representation of a node and its ancestry.
Definition: GeoNodePath.h:38
void Ar42Gamma4(std::vector< std::tuple< ti_PDGID, td_Mass, TLorentzVector >> &v_prods)
std::vector< std::unique_ptr< TH1D > > neutronspectrum
std::vector< std::string > fMaterial
List of regexes of materials in which to generate the decays. Example: &quot;LAr&quot;.
std::vector< double > fX0
Bottom corner x position (cm) in world coordinates.
TCEvent evt
Definition: DataStructs.cxx:8
std::vector< double > gammaintegral
std::vector< double > fBq
Radioactivity in Becquerels (decay per sec) per cubic cm.
ROOT::Math::PositionVector3D< ROOT::Math::Cartesian3D< double >, ROOT::Math::GlobalCoordinateSystemTag > Point_t
Type for representation of position in physical 3D space.
Definition: geo_vectors.h:184
RadioGen(fhicl::ParameterSet const &pset)
std::vector< std::unique_ptr< TH1D > > betaspectrum
std::vector< double > fX1
Top corner x position (cm) in world coordinates.
art framework interface to geometry description
ROOT::Math::Transform3D TransformationMatrix
Type of transformation matrix used in geometry.
bnb BNB Stream
constexpr Point origin()
Returns a origin position with a point of the specified type.
Definition: geo_vectors.h:227
std::vector< double > fY0
Bottom corner y position (cm) in world coordinates.
void produce(art::Event &evt)