SingleGen_module.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 /// \file SingleGen_module.cc
3 /// \brief Generator for cosmic-rays
4 ///
5 /// Module designed to produce a set list of particles for a MC event
6 ///
7 /// \author brebel@fnal.gov
8 ////////////////////////////////////////////////////////////////////////
9 
10 // C++ includes.
11 #include <sstream>
12 #include <string>
13 #include <cmath>
14 #include <memory>
15 #include <iterator>
16 #include <map>
17 #include <initializer_list>
18 #include <cctype> // std::tolower()
19 
20 
21 // Framework includes
24 #include "fhiclcpp/types/Name.h"
25 #include "fhiclcpp/types/Comment.h"
26 #include "fhiclcpp/types/Atom.h"
32 #include "cetlib_except/exception.h"
33 #include "cetlib/filesystem.h"
34 #include "cetlib/search_path.h"
35 
36 // nurandom includes
37 #include "nurandom/RandomUtils/NuRandomService.h"
38 
39 // nusimdata includes
42 
43 // LArSoft libraries
47 
48 
49 #include "TVector3.h"
50 #include "TDatabasePDG.h"
51 #include "TFile.h"
52 #include "TH1.h"
53 #include "TH2.h"
54 
55 #include "CLHEP/Random/RandFlat.h"
56 #include "CLHEP/Random/RandGaussQ.h"
57 
58 namespace evgen {
59 
60  /// module to produce single or multiple specified particles in the detector
61  class SingleGen : public art::EDProducer {
62 
63  public:
64 
65  struct Config {
66  using Name = fhicl::Name;
68 
70  Name("ParticleSelectionMode"),
71  Comment("generate one particle, or one particle per PDG ID: " + presentOptions(ParticleSelectionModeNames))
72  };
73 
75  Name("PadOutVectors"),
76  Comment("if true, all per-PDG-ID quantities must contain only one value, which is then used for all PDG IDs")
77  };
78 
80  Name("PDG"),
81  Comment("PDG ID of the particles to be generated; this is the key for the other options marked as \"per PDG ID\"")
82  };
83 
85  Name("PDist"),
86  Comment("momentum distribution type: " + presentOptions(DistributionNames)),
88  };
89 
91  Name("P0"),
92  Comment("central momentum (GeV/c) to generate"),
93  [this](){ return !fromHistogram(PDist()); }
94  };
95 
97  Name("SigmaP"),
98  Comment("variation in momenta (GeV/c)"),
99  [this](){ return !fromHistogram(PDist()); }
100  };
101 
103  Name("X0"),
104  Comment("central x position (cm) in world coordinates [per PDG ID]")
105  };
106 
108  Name("Y0"),
109  Comment("central y position (cm) in world coordinates [per PDG ID]")
110  };
111 
113  Name("Z0"),
114  Comment("central z position (cm) in world coordinates [per PDG ID]")
115  };
116 
118  Name("T0"),
119  Comment("central time (s) [per PDG ID]")
120  };
121 
123  Name("SigmaX"),
124  Comment("variation (radius or RMS) in x position (cm) [per PDG ID]")
125  };
126 
128  Name("SigmaY"),
129  Comment("variation (radius or RMS) in y position (cm) [per PDG ID]")
130  };
131 
133  Name("SigmaZ"),
134  Comment("variation (radius or RMS) in z position (cm) [per PDG ID]")
135  };
136 
138  Name("SigmaT"),
139  Comment("variation (semi-interval or RMS) in time (s) [per PDG ID]")
140  };
141 
143  Name("PosDist"),
144  Comment("distribution of starting position: " + presentOptions(DistributionNames, true, { kHIST }))
145  };
146 
148  Name("TDist"),
149  Comment("time distribution type: " + presentOptions(DistributionNames, true, { kHIST }))
150  };
151 
153  Name("SingleVertex"),
154  Comment("if true, all particles are produced at the same location"),
155  false
156  };
157 
159  Name("Theta0XZ"),
160  Comment("angle from Z axis on world X-Z plane (degrees)")
161  };
162 
164  Name("Theta0YZ"),
165  Comment("angle from Z axis on world Y-Z plane (degrees)")
166  };
167 
169  Name("SigmaThetaXZ"),
170  Comment("variation in angle in X-Z plane (degrees)")
171  };
172 
174  Name("SigmaThetaYZ"),
175  Comment("variation in angle in Y-Z plane (degrees)")
176  };
177 
179  Name("AngleDist"),
180  Comment("angular distribution type: " + presentOptions(DistributionNames)),
182  };
183 
185  Name("HistogramFile"),
186  Comment("ROOT file containing the required distributions for the generation"),
187  [this](){ return fromHistogram(AngleDist()) || fromHistogram(PDist()); }
188  };
189 
191  Name("PHist"),
192  Comment("name of the histograms of momentum distributions"),
193  [this](){ return fromHistogram(PDist()); }
194  };
195 
196  /*
197  fhicl::Sequence<std::string> ThetaPhiHist{
198  Name("ThetaPhiHist"),
199  Comment("name of the histograms of angular (theta/phi) distribution"),
200  [this](){ return fromHistogram(AngleDist()); }
201  };
202  */
204  Name("ThetaXzYzHist"),
205  Comment("name of the histograms of angular (X-Z and Y-Z) distribution"),
206  [this](){ return fromHistogram(AngleDist()); }
207  };
208 
210  Name("Seed"),
211  Comment("override the random number generator seed")
212  };
213 
214 
215  private:
216 
217  /// Returns whether the specified mode is an histogram distribution.
218  bool fromHistogram(std::string const& key) const;
219 
220  }; // struct Config
221 
222 
224 
225 
226  explicit SingleGen(Parameters const& config);
227 
228  // This is called for each event.
229  void produce(art::Event& evt) override;
230 
231  private:
232 
233  /// Names of all particle selection modes.
234  static const std::map<int, std::string> ParticleSelectionModeNames;
235  /// Names of all distribution modes.
236  static const std::map<int, std::string> DistributionNames;
237 
238  void SampleOne(unsigned int i,
239  simb::MCTruth &mct);
240  void SampleMany(simb::MCTruth &mct);
241  void Sample(simb::MCTruth &mct);
242  void printVecs(std::vector<std::string> const& list);
243  bool PadVector(std::vector<double> &vec);
244  double SelectFromHist(const TH1& h);
245  void SelectFromHist(const TH2& h, double &x, double &y);
246 
247  /// @{
248  /// @name Constants for particle type extraction mode (`ParticleSelectionMode` parameter).
249 
250  static constexpr int kSelectAllParts = 0; ///< One particle per entry is generated
251  static constexpr int kSelectOneRandPart = 1; ///< One particle is generated, extracted from the provided options.
252  /// @}
253 
254  /// @{
255  /// @name Constants for kinematic distribution options.
256 
257  static constexpr int kUNIF = 0; ///< Uniform distribution.
258  static constexpr int kGAUS = 1; ///< Gaussian distribution.
259  static constexpr int kHIST = 2; ///< Distribution from histograms.
260  /// @}
261 
262  int fMode; ///< Particle Selection Mode
263  ///< 0--generate a list of all particles,
264  ///< 1--generate a single particle selected randomly from the list
265  bool fPadOutVectors; ///< Select to pad out configuration vectors if they are not of
266  ///< of the same length as PDG
267  ///< false: don't pad out - all values need to specified
268  ///< true: pad out - default values assumed and printed out
269  std::vector<int> fPDG; ///< PDG code of particles to generate
270  std::vector<double> fP0; ///< Central momentum (GeV/c) to generate
271  std::vector<double> fSigmaP; ///< Variation in momenta (GeV/c)
272  int fPDist; ///< How to distribute momenta (gaus or uniform)
273  std::vector<double> fX0; ///< Central x position (cm) in world coordinates
274  std::vector<double> fY0; ///< Central y position (cm) in world coordinates
275  std::vector<double> fZ0; ///< Central z position (cm) in world coordinates
276  std::vector<double> fT0; ///< Central t position (s) in world coordinates
277  std::vector<double> fSigmaX; ///< Variation in x position (cm)
278  std::vector<double> fSigmaY; ///< Variation in y position (cm)
279  std::vector<double> fSigmaZ; ///< Variation in z position (cm)
280  std::vector<double> fSigmaT; ///< Variation in t position (s)
281  int fPosDist; ///< How to distribute xyz (gaus, or uniform)
282  int fTDist; ///< How to distribute t (gaus, or uniform)
283  bool fSingleVertex; ///< if true - all particles produced at the same location
284  std::vector<double> fTheta0XZ; ///< Angle in XZ plane (degrees)
285  std::vector<double> fTheta0YZ; ///< Angle in YZ plane (degrees)
286  std::vector<double> fSigmaThetaXZ; ///< Variation in angle in XZ plane
287  std::vector<double> fSigmaThetaYZ; ///< Variation in angle in YZ plane
288  int fAngleDist; ///< How to distribute angles (gaus, uniform)
289  std::string fHistFileName; ///< Filename containing histogram of momenta
290  std::vector<std::string> fPHist; ///< name of histogram of momenta
291  std::vector<std::string> fThetaXzYzHist; ///< name of histogram for thetaxz/thetayz distribution
292 
293  std::vector<std::unique_ptr<TH1>> hPHist ; /// actual TH1 for momentum distributions
294  std::vector<std::unique_ptr<TH2>> hThetaXzYzHist ; /// actual TH2 for angle distributions - Xz on x axis .
295  // FYI - thetaxz and thetayz are related to standard polar angles as follows:
296  // thetaxz = atan2(math.sin(theta) * cos(phi), cos(theta))
297  // thetayz = asin(sin(theta) * sin(phi));
298 
299  CLHEP::HepRandomEngine& fEngine; ///< art-managed random-number engine
300 
301 
302  /// Returns a vector with the name of particle selection mode keywords.
303  static std::map<int, std::string> makeParticleSelectionModeNames();
304 
305  /// Returns a vector with the name of distribution keywords.
306  static std::map<int, std::string> makeDistributionNames();
307 
308 
309  /// Act on begin of run: write "RunData" information (`sumdata::RunData`).
310  void beginRun(art::Run& run) override;
311 
312 
313  /// Performs checks and initialization based on the current configuration.
314  void setup();
315 
316  /**
317  * @brief Parses an option string and returns the corresponding option number.
318  * @tparam OptionList type of list of options (e.g. `std::map<int, std::string>`)
319  * @param Option the string of the option to be parsed
320  * @param allowedOptions list of valid options, as key/name pairs
321  * @return the key of the `Option` string from `allowedOptions`
322  * @throws std::runtime_error if `Option` is not in the option list
323  *
324  * The option string `Option` represent a single one among the supported
325  * options as defined in `allowedOptions`. The option string can be either
326  * one of the option names (the matching is not case-sensitive) or the
327  * number of the option itself.
328  *
329  * `OptionList` requirements
330  * --------------------------
331  *
332  * `OptionList` must behave like a sequence with forward iterators.
333  * Each element must behave as a pair, whose first element is the option key
334  * and the second element is the option name, equivalent to a string in that
335  * it must be forward-iterable and its elements can be converted by
336  * `std::tolower()`. The key type has no requirements beside being copiable.
337  */
338  template <typename OptionList>
339  static auto selectOption
340  (std::string Option, OptionList const& allowedOptions) -> decltype(auto);
341 
342  /**
343  * @brief Returns a string describing all options in the list
344  * @tparam OptionList type of list of options (e.g. `std::map<int, std::string>`)
345  * @param allowedOptions the list of allowed options
346  * @param printKey whether to print the key of the option beside its name
347  * @param excludeKeys list of keys to be ignored (none by default)
348  * @return a string with all options in a line
349  *
350  * The result string is a list of option names, separated by commas, like in
351  * `"'apple', 'orange', 'banana'"`. If `printKey` is `true`, the key of each
352  * option is also written in parentheses, like in
353  * `"'apple' (1), 'orange' (7), 'banana' (2)"`.
354  */
355  template <typename OptionList>
357  OptionList const& allowedOptions, bool printKey,
358  std::initializer_list<typename OptionList::value_type::first_type> exclude
359  );
360 
361  template <typename OptionList>
363  (OptionList const& allowedOptions, bool printKey = true)
364  { return presentOptions(allowedOptions, printKey, {}); }
365 
366 
367  /// Returns the name of the specified option key, or `defName` if not known.
368  template <typename OptionList>
369  static std::string optionName(
370  typename OptionList::value_type::first_type optionKey,
371  OptionList const& allowedOptions,
372  std::string defName = "<unknown>"
373  );
374 
375  }; // class SingleGen
376 }
377 
378 namespace evgen{
379 
380  std::map<int, std::string> SingleGen::makeParticleSelectionModeNames() {
381  std::map<int, std::string> names;
382  names[int(kSelectAllParts )] = "all";
383  names[int(kSelectOneRandPart)] = "singleRandom";
384  return names;
385  } // SingleGen::makeParticleSelectionModeNames()
386 
387  std::map<int, std::string> SingleGen::makeDistributionNames() {
388  std::map<int, std::string> names;
389  names[int(kUNIF)] = "uniform";
390  names[int(kGAUS)] = "Gaussian";
391  names[int(kHIST)] = "histograms";
392  return names;
393  } // SingleGen::makeDistributionNames()
394 
395  const std::map<int, std::string> SingleGen::ParticleSelectionModeNames
397  const std::map<int, std::string> SingleGen::DistributionNames
399 
400 
401  template <typename OptionList>
403  (std::string Option, OptionList const& allowedOptions) -> decltype(auto)
404  {
405  using key_type = typename OptionList::value_type::first_type;
406  using tolower_type = int(*)(int);
407  auto toLower = [](auto const& S)
408  {
409  std::string s;
410  s.reserve(S.size());
411  std::transform(S.cbegin(), S.cend(), std::back_inserter(s),
412  (tolower_type) &std::tolower);
413  return s;
414  };
415  auto option = toLower(Option);
416  for (auto const& candidate: allowedOptions) {
417  if (toLower(candidate.second) == option) return candidate.first;
418  }
419  try {
420  std::size_t end;
421  key_type num = std::stoi(Option, &end);
422  if (allowedOptions.count(num) && (end == Option.length())) return num;
423  }
424  catch (std::invalid_argument const&) {}
425  throw std::runtime_error("Option '" + Option + "' not supported.");
426  } // SingleGen::selectOption()
427 
428 
429  template <typename OptionList>
431  OptionList const& allowedOptions, bool printKey /* = true */,
432  std::initializer_list<typename OptionList::value_type::first_type> exclude /* = {} */
433  ) {
435 
436  unsigned int n = 0;
437  for (auto const& option: allowedOptions) {
438  auto const& key = option.first;
439  if (std::find(exclude.begin(), exclude.end(), key) != exclude.end())
440  continue;
441  if (n++ > 0) msg += ", ";
442  msg += '\"' + std::string(option.second) + '\"';
443  if (printKey)
444  msg += " (" + std::to_string(key) + ")";
445  } // for
446  return msg;
447  } // SingleGen::presentOptions()
448 
449 
450  template <typename OptionList>
452  typename OptionList::value_type::first_type optionKey,
453  OptionList const& allowedOptions,
454  std::string defName /* = "<unknown>" */
455  ) {
456  auto iOption = allowedOptions.find(optionKey);
457  return (iOption != allowedOptions.end())? iOption->second: defName;
458  } // SingleGen::optionName()
459 
460 
461  //____________________________________________________________________________
464  } // SingleGen::Config::fromHistogram()
465 
466  //____________________________________________________________________________
468  : EDProducer{config}
469  , fMode (selectOption(config().ParticleSelectionMode(), ParticleSelectionModeNames))
470  , fPadOutVectors(config().PadOutVectors())
471  , fPDG (config().PDG())
472  , fP0 (config().P0())
473  , fSigmaP (config().SigmaP())
475  , fX0 (config().X0())
476  , fY0 (config().Y0())
477  , fZ0 (config().Z0())
478  , fT0 (config().T0())
479  , fSigmaX (config().SigmaX())
480  , fSigmaY (config().SigmaY())
481  , fSigmaZ (config().SigmaZ())
482  , fSigmaT (config().SigmaT())
485  , fSingleVertex (config().SingleVertex())
486  , fTheta0XZ (config().Theta0XZ())
487  , fTheta0YZ (config().Theta0YZ())
488  , fSigmaThetaXZ (config().SigmaThetaXZ())
489  , fSigmaThetaYZ (config().SigmaThetaYZ())
490  , fAngleDist (selectOption(config().AngleDist(), DistributionNames))
491  , fHistFileName (config().HistogramFile())
492  , fPHist (config().PHist())
493  , fThetaXzYzHist(config().ThetaXzYzHist())
495  {
496  setup();
497  rndm::NuRandomService::seed_t seed;
498  if (config().Seed(seed)) {
499  fEngine.setSeed(seed, 0 /* dummy? */);
500  }
501 
502  produces< std::vector<simb::MCTruth> >();
503  produces< sumdata::RunData, art::InRun >();
504 
505  }
506 
507 
508  //____________________________________________________________________________
510  {
511  geo::GeometryCore const& geom = *(lar::providerFrom<geo::Geometry>());
512  run.put
513  (std::make_unique<sumdata::RunData>(geom.DetectorName()), art::fullRun());
514  }
515 
516 
517  //____________________________________________________________________________
519  {
520  // do not put seed in reconfigure because we don't want to reset
521  // the seed midstream
522  std::vector<std::string> vlist(15);
523  vlist[0] = "PDG";
524  vlist[1] = "P0";
525  vlist[2] = "SigmaP";
526  vlist[3] = "X0";
527  vlist[4] = "Y0";
528  vlist[5] = "Z0";
529  vlist[6] = "SigmaX";
530  vlist[7] = "SigmaY";
531  vlist[8] = "SigmaZ";
532  vlist[9] = "Theta0XZ";
533  vlist[10] = "Theta0YZ";
534  vlist[11] = "SigmaThetaXZ";
535  vlist[12] = "SigmaThetaYZ";
536  vlist[13] = "T0";
537  vlist[14] = "SigmaT";
538 
539 // vlist[15] = "PHist";
540 // vlist[16] = "ThetaHist";
541 // vlist[17] = "PhiHist";
542 
543  // begin tests for multiple particle error possibilities
544  std::string list;
545  if (fPDist != kHIST) {
546  if( !this->PadVector(fP0 ) ){ list.append(vlist[1].append(", \n")); }
547  if( !this->PadVector(fSigmaP ) ){ list.append(vlist[2].append(", \n")); }
548  }
549  if( !this->PadVector(fX0 ) ){ list.append(vlist[3].append(", \n")); }
550  if( !this->PadVector(fY0 ) ){ list.append(vlist[4].append(", \n")); }
551  if( !this->PadVector(fZ0 ) ){ list.append(vlist[5].append(", \n")); }
552  if( !this->PadVector(fSigmaX ) ){ list.append(vlist[6].append(", \n")); }
553  if( !this->PadVector(fSigmaY ) ){ list.append(vlist[7].append(", \n")); }
554  if( !this->PadVector(fSigmaZ ) ){ list.append(vlist[8].append(", \n")); }
555  if( !this->PadVector(fTheta0XZ ) ){ list.append(vlist[9].append(", \n")); }
556  if( !this->PadVector(fTheta0YZ ) ){ list.append(vlist[10].append(", \n")); }
557  if( !this->PadVector(fSigmaThetaXZ) ){ list.append(vlist[11].append(", \n")); }
558  if( !this->PadVector(fSigmaThetaYZ) ){ list.append(vlist[12].append(" \n")); }
559  if( !this->PadVector(fT0 ) ){ list.append(vlist[13].append(", \n")); }
560  if( !this->PadVector(fSigmaT ) ){ list.append(vlist[14].append(", \n")); }
561 
562 
563 
564  if(list.size() > 0)
565  throw cet::exception("SingleGen") << "The "<< list
566  << "\n vector(s) defined in the fhicl files has/have "
567  << "a different size than the PDG vector "
568  << "\n and it has (they have) more than one value, "
569  << "\n disallowing sensible padding "
570  << " and/or you have set fPadOutVectors to false. \n";
571 
572  if(fPDG.size() > 1 && fPadOutVectors) this->printVecs(vlist);
573 
574  // If needed, get histograms for momentum and angle distributions
575  TFile* histFile = nullptr;
576  if (!fHistFileName.empty()) {
577  if (fHistFileName[0] == '/') {
578  // We have an absolute path, use given name exactly.
580  histFile = new TFile(fHistFileName.c_str());
581  if (!histFile || histFile->IsZombie() || !histFile->IsOpen()) {
582  delete histFile;
583  histFile = nullptr;
584  throw art::Exception(art::errors::NotFound) << "Cannot open ROOT file specified in parameter HistogramFile: \"" << fHistFileName << "\"";
585  }
586  }
587  else {
588  throw art::Exception(art::errors::NotFound) << "ROOT file specified in parameter HistogramFile: \"" << fHistFileName << "\" does not exist!";
589  }
590  }
591  else {
592  // We have a relative path, search starting from current directory.
593  std::string relative_filename{"./"};
594  relative_filename += fHistFileName;
595  if (cet::file_exists(relative_filename)) {
596  histFile = new TFile(relative_filename.c_str());
597  if (!histFile || histFile->IsZombie() || !histFile->IsOpen()) {
598  delete histFile;
599  histFile = nullptr;
600  throw art::Exception(art::errors::NotFound) << "Cannot open ROOT file found using relative path and originally specified in parameter HistogramFile: \"" << relative_filename << '"';
601  }
602  }
603  else {
604  cet::search_path sp{"FW_SEARCH_PATH"};
605  std::string found_filename;
606  auto found = sp.find_file(fHistFileName, found_filename);
607  if (!found) {
608  throw art::Exception(art::errors::NotFound) << "Cannot find ROOT file in current directory nor on FW_SEARCH_PATH specified in parameter HistogramFile: \"" << fHistFileName << '"';
609  }
610  histFile = new TFile(found_filename.c_str());
611  if (!histFile || histFile->IsZombie() || !histFile->IsOpen()) {
612  delete histFile;
613  histFile = nullptr;
614  throw art::Exception(art::errors::NotFound) << "Cannot open ROOT file found on FW_SEARCH_PATH and originally specified in parameter HistogramFile: \"" << found_filename << '"';
615  }
616  }
617  }
618  }
619 
620  //
621  // deal with position distribution
622  //
623  switch (fPosDist) {
624  case kGAUS: case kUNIF: break; // supported, no further action needed
625  default:
627  << "Position distribution of type '"
629  << "' (" << std::to_string(fPosDist) << ") is not supported.";
630  } // switch(fPosDist)
631 
632  //
633  // deal with time distribution
634  //
635  switch (fTDist) {
636  case kGAUS: case kUNIF: break; // supported, no further action needed
637  default:
639  << "Time distribution of type '"
641  << "' (" << std::to_string(fTDist) << ") is not supported.";
642  } // switch(fTDist)
643 
644  //
645  // deal with momentum distribution
646  //
647  switch (fPDist) {
648  case kHIST:
649  if (fPHist.size() != fPDG.size()) {
651  << fPHist.size() << " momentum histograms to describe " << fPDG.size() << " particle types...";
652  }
653  hPHist.reserve(fPHist.size());
654  for (auto const& histName: fPHist) {
655  TH1* pHist = dynamic_cast<TH1*>(histFile->Get(histName.c_str()));
656  if (!pHist) {
658  << "Failed to read momentum histogram '" << histName << "' from '" << histFile->GetPath() << "\'";
659  }
660  pHist->SetDirectory(nullptr); // make it independent of the input file
661  hPHist.emplace_back(pHist);
662  } // for
663  break;
664  default: // supported, no further action needed
665  break;
666  } // switch(fPDist)
667 
668  switch (fAngleDist) {
669  case kHIST:
670  if (fThetaXzYzHist.size() != fPDG.size()) {
672  << fThetaXzYzHist.size() << " direction histograms to describe " << fPDG.size() << " particle types...";
673  }
674  hThetaXzYzHist.reserve(fThetaXzYzHist.size());
675  for (auto const& histName: fThetaXzYzHist) {
676  TH2* pHist = dynamic_cast<TH2*>(histFile->Get(histName.c_str()));
677  if (!pHist) {
679  << "Failed to read direction histogram '" << histName << "' from '" << histFile->GetPath() << "\'";
680  }
681  pHist->SetDirectory(nullptr); // make it independent of the input file
682  hThetaXzYzHist.emplace_back(pHist);
683  } // for
684  default: // supported, no further action needed
685  break;
686  } // switch(fAngleDist)
687 
688  delete histFile;
689 
690  }
691 
692  //____________________________________________________________________________
693  bool SingleGen::PadVector(std::vector<double> &vec)
694  {
695  // check if the vec has the same size as fPDG
696  if( vec.size() != fPDG.size() ){
697  // if not padding out the vectors always cause an
698  // exception to be thrown if the vector in question
699  // is not the same size as the fPDG vector
700  // the exception is thrown in the reconfigure method
701  // that calls this one
702  if (!fPadOutVectors) return false;
703  else if( fPadOutVectors){
704  // if padding of vectors is desired but the vector in
705  // question has more than one entry it isn't clear
706  // what the padded values should be so cause
707  // an exception
708  if(vec.size() != 1) return false;
709 
710  // pad it out
711  vec.resize(fPDG.size(), vec[0]);
712 
713  }// end if padding out vectors
714  }// end if the vector size is not the same as fPDG
715 
716  return true;
717  }
718 
719  //____________________________________________________________________________
721  {
722 
723  ///unique_ptr allows ownership to be transferred to the art::Event after the put statement
724  std::unique_ptr< std::vector<simb::MCTruth> > truthcol(new std::vector<simb::MCTruth>);
725 
726  simb::MCTruth truth;
728  Sample(truth);
729 
730  MF_LOG_DEBUG("SingleGen") << truth;
731 
732  truthcol->push_back(truth);
733 
734  evt.put(std::move(truthcol));
735 
736  return;
737  }
738 
739  //____________________________________________________________________________
740  // Draw the type, momentum and position of a single particle from the
741  // FCIHL description
742  void SingleGen::SampleOne(unsigned int i, simb::MCTruth &mct){
743 
744  CLHEP::RandFlat flat(fEngine);
745  CLHEP::RandGaussQ gauss(fEngine);
746 
747  // Choose momentum
748  double p = 0.0;
749  double m = 0.0;
750  if (fPDist == kGAUS) {
751  p = gauss.fire(fP0[i], fSigmaP[i]);
752  }
753  else if (fPDist == kHIST){
754  p = SelectFromHist(*(hPHist[i]));
755  }
756  else{// if (fPDist == kUNIF) {
757  p = fP0[i] + fSigmaP[i]*(2.0*flat.fire()-1.0);
758  }
759 // else {std::cout << "do not understand the value of PDist!";}
760 
761  static TDatabasePDG pdgt;
762  TParticlePDG* pdgp = pdgt.GetParticle(fPDG[i]);
763  if (pdgp) m = pdgp->Mass();
764 
765  // Choose position
766  TVector3 x;
767  if (fPosDist == kGAUS) {
768  x[0] = gauss.fire(fX0[i], fSigmaX[i]);;
769  x[1] = gauss.fire(fY0[i], fSigmaY[i]);
770  x[2] = gauss.fire(fZ0[i], fSigmaZ[i]);
771  }
772  else {
773  x[0] = fX0[i] + fSigmaX[i]*(2.0*flat.fire()-1.0);
774  x[1] = fY0[i] + fSigmaY[i]*(2.0*flat.fire()-1.0);
775  x[2] = fZ0[i] + fSigmaZ[i]*(2.0*flat.fire()-1.0);
776  }
777 
778  double t = 0.;
779  if(fTDist==kGAUS){
780  t = gauss.fire(fT0[i], fSigmaT[i]);
781  }
782  else{
783  t = fT0[i] + fSigmaT[i]*(2.0*flat.fire()-1.0);
784  }
785 
786  TLorentzVector pos(x[0], x[1], x[2], t);
787 
788  // Choose angles
789  double thxz = 0;
790  double thyz = 0;
791 
792  double thyzrads = 0;
793  double thyzradsplussigma = 0;
794  double thyzradsminussigma = 0;
795 
796  if (fAngleDist == kGAUS) {
797  thxz = gauss.fire(fTheta0XZ[i], fSigmaThetaXZ[i]);
798  thyz = gauss.fire(fTheta0YZ[i], fSigmaThetaYZ[i]);
799  }
800  else if (fAngleDist == kHIST){ // Select thetaxz and thetayz from histogram
801  double thetaxz = 0;
802  double thetayz = 0;
803  SelectFromHist(*(hThetaXzYzHist[i]), thetaxz, thetayz);
804  thxz = (180./M_PI)*thetaxz;
805  thyz = (180./M_PI)*thetayz;
806  }
807  else {
808 
809  // Choose angles flat in phase space, which is flat in theta_xz
810  // and flat in sin(theta_yz).
811 
812  thxz = fTheta0XZ[i] + fSigmaThetaXZ[i]*(2.0*flat.fire()-1.0);
813 
814  thyzrads = std::asin(std::sin((M_PI/180.)*(fTheta0YZ[i]))); //Taking asin of sin gives value between -Pi/2 and Pi/2 regardless of user input
815  thyzradsplussigma = TMath::Min((thyzrads + ((M_PI/180.)*fabs(fSigmaThetaYZ[i]))), M_PI/2.);
816  thyzradsminussigma = TMath::Max((thyzrads - ((M_PI/180.)*fabs(fSigmaThetaYZ[i]))), -M_PI/2.);
817 
818  //uncomment line to print angular variation info
819  //std::cout << "Central angle: " << (180./M_PI)*thyzrads << " Max angle: " << (180./M_PI)*thyzradsplussigma << " Min angle: " << (180./M_PI)*thyzradsminussigma << std::endl;
820 
821  double sinthyzmin = std::sin(thyzradsminussigma);
822  double sinthyzmax = std::sin(thyzradsplussigma);
823  double sinthyz = sinthyzmin + flat.fire() * (sinthyzmax - sinthyzmin);
824  thyz = (180. / M_PI) * std::asin(sinthyz);
825  }
826 
827  double thxzrad=thxz*M_PI/180.0;
828  double thyzrad=thyz*M_PI/180.0;
829 
830  TLorentzVector pvec(p*std::cos(thyzrad)*std::sin(thxzrad),
831  p*std::sin(thyzrad),
832  p*std::cos(thxzrad)*std::cos(thyzrad),
833  std::sqrt(p*p+m*m));
834 
835  // set track id to -i as these are all primary particles and have id <= 0
836  int trackid = -1*(i+1);
837  std::string primary("primary");
838 
839  simb::MCParticle part(trackid, fPDG[i], primary);
840  part.AddTrajectoryPoint(pos, pvec);
841 
842  //std::cout << "Px: " << pvec.Px() << " Py: " << pvec.Py() << " Pz: " << pvec.Pz() << std::endl;
843  //std::cout << "x: " << pos.X() << " y: " << pos.Y() << " z: " << pos.Z() << " time: " << pos.T() << std::endl;
844  //std::cout << "YZ Angle: " << (thyzrad * (180./M_PI)) << " XZ Angle: " << (thxzrad * (180./M_PI)) << std::endl;
845 
846  mct.Add(part);
847  }
848 
849  //____________________________________________________________________________
850  // Draw the type, momentum and position for all particles from the
851  // FCIHL description. Start positions will all match but momenta and angles drawn from
852  // distributions defined in the fhicls
854 
855  CLHEP::RandFlat flat(fEngine);
856  CLHEP::RandGaussQ gauss(fEngine);
857 
858  // Choose position
859  TVector3 x;
860  if (fPosDist == kGAUS) {
861  x[0] = gauss.fire(fX0[0], fSigmaX[0]);;
862  x[1] = gauss.fire(fY0[0], fSigmaY[0]);
863  x[2] = gauss.fire(fZ0[0], fSigmaZ[0]);
864  }
865  else {
866  x[0] = fX0[0] + fSigmaX[0]*(2.0*flat.fire()-1.0);
867  x[1] = fY0[0] + fSigmaY[0]*(2.0*flat.fire()-1.0);
868  x[2] = fZ0[0] + fSigmaZ[0]*(2.0*flat.fire()-1.0);
869  }
870 
871  double t = 0.;
872  if(fTDist==kGAUS){
873  t = gauss.fire(fT0[0], fSigmaT[0]);
874  }
875  else{
876  t = fT0[0] + fSigmaT[0]*(2.0*flat.fire()-1.0);
877  }
878 
879  TLorentzVector pos(x[0], x[1], x[2], t);
880 
881  // loop through particles and select momenta and angles
882  for (unsigned int i(0); i<fPDG.size(); ++i){
883  // Choose momentum
884  double p = 0.0;
885  double m = 0.0;
886  if (fPDist == kGAUS) {
887  p = gauss.fire(fP0[i], fSigmaP[i]);
888  }
889  else if (fPDist == kHIST){
890  p = SelectFromHist(*(hPHist[i]));
891  }
892  else {
893  p = fP0[i] + fSigmaP[i]*(2.0*flat.fire()-1.0);
894  }
895 
896  static TDatabasePDG pdgt;
897  TParticlePDG* pdgp = pdgt.GetParticle(fPDG[i]);
898  if (pdgp) m = pdgp->Mass();
899 
900 
901  // Choose angles
902  double thxz = 0;
903  double thyz = 0;
904 
905  double thyzrads = 0;
906  double thyzradsplussigma = 0;
907  double thyzradsminussigma = 0;
908 
909  if (fAngleDist == kGAUS) {
910  thxz = gauss.fire(fTheta0XZ[i], fSigmaThetaXZ[i]);
911  thyz = gauss.fire(fTheta0YZ[i], fSigmaThetaYZ[i]);
912  }
913  else if (fAngleDist == kHIST){
914  double thetaxz = 0;
915  double thetayz = 0;
916  SelectFromHist(*(hThetaXzYzHist[i]), thetaxz, thetayz);
917  thxz = (180./M_PI)*thetaxz;
918  thyz = (180./M_PI)*thetayz;
919  }
920  else {
921 
922  // Choose angles flat in phase space, which is flat in theta_xz
923  // and flat in sin(theta_yz).
924 
925  thxz = fTheta0XZ[i] + fSigmaThetaXZ[i]*(2.0*flat.fire()-1.0);
926 
927  thyzrads = std::asin(std::sin((M_PI/180.)*(fTheta0YZ[i]))); //Taking asin of sin gives value between -Pi/2 and Pi/2 regardless of user input
928  thyzradsplussigma = TMath::Min((thyzrads + ((M_PI/180.)*fabs(fSigmaThetaYZ[i]))), M_PI/2.);
929  thyzradsminussigma = TMath::Max((thyzrads - ((M_PI/180.)*fabs(fSigmaThetaYZ[i]))), -M_PI/2.);
930 
931  //uncomment line to print angular variation info
932  //std::cout << "Central angle: " << (180./M_PI)*thyzrads << " Max angle: " << (180./M_PI)*thyzradsplussigma << " Min angle: " << (180./M_PI)*thyzradsminussigma << std::endl;
933 
934  double sinthyzmin = std::sin(thyzradsminussigma);
935  double sinthyzmax = std::sin(thyzradsplussigma);
936  double sinthyz = sinthyzmin + flat.fire() * (sinthyzmax - sinthyzmin);
937  thyz = (180. / M_PI) * std::asin(sinthyz);
938  }
939 
940  double thxzrad=thxz*M_PI/180.0;
941  double thyzrad=thyz*M_PI/180.0;
942 
943  TLorentzVector pvec(p*std::cos(thyzrad)*std::sin(thxzrad),
944  p*std::sin(thyzrad),
945  p*std::cos(thxzrad)*std::cos(thyzrad),
946  std::sqrt(p*p+m*m));
947 
948  // set track id to -i as these are all primary particles and have id <= 0
949  int trackid = -1*(i+1);
950  std::string primary("primary");
951 
952  simb::MCParticle part(trackid, fPDG[i], primary);
953  part.AddTrajectoryPoint(pos, pvec);
954 
955  //std::cout << "Px: " << pvec.Px() << " Py: " << pvec.Py() << " Pz: " << pvec.Pz() << std::endl;
956  //std::cout << "x: " << pos.X() << " y: " << pos.Y() << " z: " << pos.Z() << " time: " << pos.T() << std::endl;
957  //std::cout << "YZ Angle: " << (thyzrad * (180./M_PI)) << " XZ Angle: " << (thxzrad * (180./M_PI)) << std::endl;
958  mct.Add(part);
959  }
960  }
961 
962 
963  //____________________________________________________________________________
965  {
966 
967  switch (fMode) {
968  case 0: // List generation mode: every event will have one of each
969  // particle species in the fPDG array
970  if (fSingleVertex){
971  SampleMany(mct);
972  }
973  else{
974  for (unsigned int i=0; i<fPDG.size(); ++i) {
975  SampleOne(i,mct);
976  }//end loop over particles
977  }
978  break;
979  case 1: // Random selection mode: every event will exactly one particle
980  // selected randomly from the fPDG array
981  {
982  CLHEP::RandFlat flat(fEngine);
983 
984  unsigned int i=flat.fireInt(fPDG.size());
985  SampleOne(i,mct);
986  }
987  break;
988  default:
989  mf::LogWarning("UnrecognizeOption") << "SingleGen does not recognize ParticleSelectionMode "
990  << fMode;
991  break;
992  } // switch on fMode
993 
994  return;
995  }
996 
997  //____________________________________________________________________________
998  void SingleGen::printVecs(std::vector<std::string> const& list)
999  {
1000 
1001  mf::LogInfo("SingleGen") << " You are using vector values for SingleGen configuration.\n "
1002  << " Some of the configuration vectors may have been padded out ,"
1003  << " because they (weren't) as long as the pdg vector"
1004  << " in your configuration. \n"
1005  << " The new input particle configuration is:\n" ;
1006 
1008  for(size_t i = 0; i <=1; ++i){// list.size(); ++i){
1009 
1010  values.append(list[i]);
1011  values.append(": [ ");
1012 
1013  for(size_t e = 0; e < fPDG.size(); ++e){
1014  std::stringstream buf;
1015  buf.width(10);
1016  if(i == 0 ) buf << fPDG[e] << ", ";
1017  buf.precision(5);
1018  if(i == 1 ) buf << fP0[e] << ", ";
1019  if(i == 2 ) buf << fSigmaP[e] << ", ";
1020  if(i == 3 ) buf << fX0[e] << ", ";
1021  if(i == 4 ) buf << fY0[e] << ", ";
1022  if(i == 5 ) buf << fZ0[e] << ", ";
1023  if(i == 6 ) buf << fSigmaX[e] << ", ";
1024  if(i == 7 ) buf << fSigmaY[e] << ", ";
1025  if(i == 8 ) buf << fSigmaZ[e] << ", ";
1026  if(i == 9 ) buf << fTheta0XZ[e] << ", ";
1027  if(i == 10) buf << fTheta0YZ[e] << ", ";
1028  if(i == 11) buf << fSigmaThetaXZ[e] << ", ";
1029  if(i == 12) buf << fSigmaThetaYZ[e] << ", ";
1030  if(i == 13) buf << fT0[e] << ", ";
1031  if(i == 14) buf << fSigmaT[e] << ", ";
1032  values.append(buf.str());
1033  }
1034 
1035  values.erase(values.find_last_of(","));
1036  values.append(" ] \n");
1037 
1038  }// end loop over vector names in list
1039 
1040  mf::LogInfo("SingleGen") << values;
1041 
1042  return;
1043  }
1044 
1045 
1046  //____________________________________________________________________________
1047  double SingleGen::SelectFromHist(const TH1& h) // select from a 1D histogram
1048  {
1049  CLHEP::RandFlat flat(fEngine);
1050 
1051  double throw_value = h.Integral() * flat.fire();
1052  double cum_value(0);
1053  for (int i(0); i < h.GetNbinsX()+1; ++i){
1054  cum_value += h.GetBinContent(i);
1055  if (throw_value < cum_value){
1056  return flat.fire()*h.GetBinWidth(i) + h.GetBinLowEdge(i);
1057  }
1058  }
1059  return throw_value; // for some reason we've gone through all bins and failed?
1060  }
1061  //____________________________________________________________________________
1062  void SingleGen::SelectFromHist(const TH2& h, double &x, double &y) // select from a 2D histogram
1063  {
1064  CLHEP::RandFlat flat(fEngine);
1065 
1066  double throw_value = h.Integral() * flat.fire();
1067  double cum_value(0);
1068  for (int i(0); i < h.GetNbinsX()+1; ++i){
1069  for (int j(0); j < h.GetNbinsY()+1; ++j){
1070  cum_value += h.GetBinContent(i, j);
1071  if (throw_value < cum_value){
1072  x = flat.fire()*h.GetXaxis()->GetBinWidth(i) + h.GetXaxis()->GetBinLowEdge(i);
1073  y = flat.fire()*h.GetYaxis()->GetBinWidth(j) + h.GetYaxis()->GetBinLowEdge(j);
1074  return;
1075  }
1076  }
1077  }
1078  return; // for some reason we've gone through all bins and failed?
1079  }
1080  //____________________________________________________________________________
1081 
1082 
1083 }//end namespace evgen
1084 
1085 namespace evgen{
1086 
1088 
1089 }//end namespace evgen
int fPDist
How to distribute momenta (gaus or uniform)
end
while True: pbar.update(maxval-len(onlies[E][S])) #print iS, "/", len(onlies[E][S]) found = False for...
static const std::map< int, std::string > DistributionNames
Names of all distribution modes.
base_engine_t & createEngine(seed_t seed)
int fTDist
How to distribute t (gaus, or uniform)
module to produce single or multiple specified particles in the detector
bool PadVector(std::vector< double > &vec)
static std::string presentOptions(OptionList const &allowedOptions, bool printKey, std::initializer_list< typename OptionList::value_type::first_type > exclude)
Returns a string describing all options in the list.
void AddTrajectoryPoint(TLorentzVector const &position, TLorentzVector const &momentum)
Definition: MCParticle.h:257
constexpr auto fullRun()
fhicl::Sequence< double > SigmaT
void msg(const char *fmt,...)
Definition: message.cpp:107
static std::map< int, std::string > makeParticleSelectionModeNames()
Returns a vector with the name of particle selection mode keywords.
static std::map< int, std::string > makeDistributionNames()
Returns a vector with the name of distribution keywords.
void SetOrigin(simb::Origin_t origin)
Definition: MCTruth.h:82
fhicl::Sequence< double > SigmaP
std::vector< std::unique_ptr< TH2 > > hThetaXzYzHist
actual TH1 for momentum distributions
std::vector< double > fSigmaT
Variation in t position (s)
std::string string
Definition: nybbler.cc:12
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
fhicl::Sequence< double > SigmaThetaXZ
EDProducer(fhicl::ParameterSet const &pset)
Definition: EDProducer.h:20
std::vector< std::unique_ptr< TH1 > > hPHist
ChannelGroupService::Name Name
Definition: 044_section.h:5
fhicl::OptionalAtom< rndm::NuRandomService::seed_t > Seed
std::vector< std::string > fThetaXzYzHist
name of histogram for thetaxz/thetayz distribution
fhicl::Sequence< double > Y0
void SampleOne(unsigned int i, simb::MCTruth &mct)
bool fSingleVertex
if true - all particles produced at the same location
fhicl::Sequence< double > SigmaThetaYZ
fhicl::Sequence< double > SigmaZ
Particle class.
fhicl::Sequence< std::string > ThetaXzYzHist
int fPosDist
How to distribute xyz (gaus, or uniform)
void printVecs(std::vector< std::string > const &list)
fhicl::Sequence< double > P0
art framework interface to geometry description
Definition: Run.h:17
fhicl::Sequence< double > SigmaY
std::vector< double > fSigmaZ
Variation in z position (cm)
fhicl::Sequence< double > X0
static constexpr int kSelectOneRandPart
fhicl::Sequence< double > Theta0YZ
const uint PDG
Definition: qregexp.cpp:140
fhicl::Sequence< double > T0
fhicl::Atom< std::string > HistogramFile
std::vector< double > fT0
Central t position (s) in world coordinates.
fhicl::Atom< std::string > AngleDist
void produce(art::Event &evt) override
const double e
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:67
std::string DetectorName() const
Returns a string with the name of the detector, as configured.
def key(type, name=None)
Definition: graph.py:13
std::vector< double > fP0
Central momentum (GeV/c) to generate.
static Config * config
Definition: config.cpp:1054
std::vector< double > fTheta0YZ
Angle in YZ plane (degrees)
std::void_t< T > n
single particles thrown at the detector
Definition: MCTruth.h:26
def move(depos, offset)
Definition: depos.py:107
fhicl::Atom< bool > SingleVertex
p
Definition: test.py:223
fhicl::Sequence< double > SigmaX
ProductID put(std::unique_ptr< PROD > &&edp, std::string const &instance={})
Definition: DataViewImpl.h:686
fhicl::Atom< std::string > ParticleSelectionMode
static constexpr int kSelectAllParts
One particle per entry is generated.
Q_UINT16 values[128]
double SelectFromHist(const TH1 &h)
void Sample(simb::MCTruth &mct)
fhicl::Sequence< int > PDG
Description of geometry of one entire detector.
static auto selectOption(std::string Option, OptionList const &allowedOptions) -> decltype(auto)
Parses an option string and returns the corresponding option number.
fhicl::Sequence< double > Theta0XZ
fhicl::Sequence< double > Z0
std::vector< double > fY0
Central y position (cm) in world coordinates.
std::vector< int > fPDG
PDG code of particles to generate.
#define M_PI
Definition: includeROOT.h:54
cet::coded_exception< errors::ErrorCodes, ExceptionDetail::translate > Exception
Definition: Exception.h:66
std::vector< double > fSigmaThetaYZ
Variation in angle in YZ plane.
std::vector< double > fSigmaP
Variation in momenta (GeV/c)
void Add(simb::MCParticle const &part)
Definition: MCTruth.h:80
fhicl::Sequence< std::string > PHist
#define Comment
std::vector< double > fZ0
Central z position (cm) in world coordinates.
std::vector< std::string > fPHist
name of histogram of momenta
std::vector< double > fSigmaX
Variation in x position (cm)
fhicl::Atom< std::string > PosDist
static constexpr int kGAUS
Gaussian distribution.
bool fromHistogram(std::string const &key) const
Returns whether the specified mode is an histogram distribution.
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
#define MF_LOG_DEBUG(id)
std::vector< double > fX0
Central x position (cm) in world coordinates.
static std::string optionName(typename OptionList::value_type::first_type optionKey, OptionList const &allowedOptions, std::string defName="<unknown>")
Returns the name of the specified option key, or defName if not known.
Access the description of detector geometry.
list x
Definition: train.py:276
static const std::map< int, std::string > ParticleSelectionModeNames
Names of all particle selection modes.
bool file_exists(std::string const &qualified_filename)
Definition: filesystem.cc:14
std::string fHistFileName
Filename containing histogram of momenta.
int fAngleDist
How to distribute angles (gaus, uniform)
SingleGen(Parameters const &config)
static constexpr int kUNIF
Uniform distribution.
std::vector< double > fTheta0XZ
Angle in XZ plane (degrees)
TCEvent evt
Definition: DataStructs.cxx:7
Event generator information.
Definition: MCTruth.h:32
void beginRun(art::Run &run) override
Act on begin of run: write "RunData" information (sumdata::RunData).
static std::vector< std::string > const names
Definition: FragmentType.hh:8
static constexpr int kHIST
void setup()
Performs checks and initialization based on the current configuration.
fhicl::Atom< std::string > PDist
Event Generation using GENIE, cosmics or single particles.
fhicl::Atom< bool > PadOutVectors
std::string to_string(ModuleType const mt)
Definition: ModuleType.h:34
static QCString * s
Definition: config.cpp:1042
std::vector< double > fSigmaThetaXZ
Variation in angle in XZ plane.
void SampleMany(simb::MCTruth &mct)
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
fhicl::Atom< std::string > TDist
CLHEP::HepRandomEngine & fEngine
actual TH2 for angle distributions - Xz on x axis .
std::vector< double > fSigmaY
Variation in y position (cm)