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