All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MultiPartRain_module.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 // Class: MultiPartRain
3 // Module Type: producer
4 // File: MultiPartRain_module.cc
5 //
6 // Generated at Tue Dec 13 15:48:59 2016 by Kazuhiro Terao using artmod
7 // from cetpkgsupport v1_11_00.
8 ////////////////////////////////////////////////////////////////////////
9 
10 #include "art/Framework/Core/EDProducer.h"
11 #include "art/Framework/Core/ModuleMacros.h"
12 #include "art/Framework/Principal/Event.h"
13 #include "art/Framework/Principal/Handle.h"
14 #include "art/Framework/Principal/Run.h"
15 #include "art/Framework/Principal/SubRun.h"
16 //#include "art/Utilities/InputTag.h"
17 #include "fhiclcpp/ParameterSet.h"
18 #include "messagefacility/MessageLogger/MessageLogger.h"
19 
20 #include <memory>
21 #include <vector>
22 #include <cmath>
23 #include "cetlib/pow.h"
24 
25 #include "CLHEP/Random/RandFlat.h"
26 #include "CLHEP/Random/RandGauss.h"
27 #include "CLHEP/Random/RandGeneral.h"
28 
29 #include "TRandom.h"
30 #include "nurandom/RandomUtils/NuRandomService.h"
35 
36 #include "nusimdata/SimulationBase/MCTruth.h"
37 #include "nusimdata/SimulationBase/MCParticle.h"
38 
39 #include "TLorentzVector.h"
40 #include "TDatabasePDG.h"
41 
42 struct PartGenParam {
43  std::vector<int > pdg;
44  std::vector<double > mass;
45  std::array <size_t, 2 > multi;
46  std::array <double, 2 > kerange;
47  bool use_mom;
49  double weight;
50 };
51 
52 class MultiPartRain;
53 
54 class MultiPartRain : public art::EDProducer {
55 public:
56  explicit MultiPartRain(fhicl::ParameterSet const & p);
57  // The destructor generated by the compiler is fine for classes
58  // without bare pointers or other resource use.
60 
61  // Plugins should not be copied or assigned.
62  MultiPartRain(MultiPartRain const &) = delete;
63  MultiPartRain(MultiPartRain &&) = delete;
64  MultiPartRain & operator = (MultiPartRain const &) = delete;
66 
67  // Required functions.
68  void produce(art::Event & e) override;
69 
70  void beginRun(art::Run& run) override;
71 
72  void GenPosition(double& x, double& y, double& z);
73 
74  std::array<double, 3U> extractDirection() const;
75  void GenMomentum(const PartGenParam& param, const double& mass, double& px, double& py, double& pz,
76  const double x, const double y, const double z);
77 
78  std::vector<size_t> GenParticles() const;
79 
80 private:
81 
82  CLHEP::HepRandomEngine& fFlatEngine;
83  std::unique_ptr<CLHEP::RandFlat> fFlatRandom;
84  std::unique_ptr<CLHEP::RandGauss> fNormalRandom;
85  std::unique_ptr<CLHEP::RandGeneral> fCosmicAngleRandom;
86 
87  // exception thrower
88  void abort(const std::string msg) const;
89 
90  // array of particle info for generation
91  std::vector<PartGenParam> _param_v;
92 
93  // g4 time of generation
94  double _t0;
95  double _t0_sigma;
96 
97  // g4 position
98  std::array<double,2> _xrange;
99  std::array<double,2> _yrange;
100  std::array<double,2> _zrange;
101 
102  // Whether to use uniform or cosmic-like angle distribution
104 
105  // TPC array
106  std::vector<std::vector<unsigned short> > _tpc_v;
107 
108  // multiplicity constraint
109  size_t _multi_min;
110  size_t _multi_max;
111 
112  // verbosity flag
113  unsigned short _debug;
114 };
115 
116 void MultiPartRain::abort(const std::string msg) const
117 {
118  std::cerr << "\033[93m" << msg.c_str() << "\033[00m" << std::endl;
119  throw std::exception();
120 }
121 
123 { }
124 
125 MultiPartRain::MultiPartRain(fhicl::ParameterSet const & p)
126 : EDProducer(p)
127 , fFlatEngine(art::ServiceHandle<rndm::NuRandomService>()->createEngine(*this, "HepJamesRandom", "GenRain"))
128  //, fFlatEngine(art::ServiceHandle<rndm::NuRandomService>()->createEngine(*this, "HepJamesRandom", "Gen", p, "Seed"))
129 // :
130 // Initialize member data here.
131 {
132 
133  //
134  // Random engine initialization
135  //
136  fFlatRandom = std::make_unique<CLHEP::RandFlat>(fFlatEngine,0,1);
137  fNormalRandom = std::make_unique<CLHEP::RandGauss>(fFlatEngine);
138 
139  // x^2 distribution for fCosmicAngleRandom
140  // will be used to draw cos(theta)
141  const int nbins(100);
142  double parent[nbins];
143  for (size_t idx = 0; idx < nbins; ++idx) {
144  //parent[idx] = cet::square(cos(((float) idx)/nbins * util::pi()));
145  parent[idx] = cet::square(((float) idx)/nbins);
146  }
147  fCosmicAngleRandom = std::make_unique<CLHEP::RandGeneral>(fFlatEngine, parent, nbins);
148 
149  produces< std::vector<simb::MCTruth> >();
150  produces< sumdata::RunData, art::InRun >();
151 
152  _debug = p.get<unsigned short>("DebugMode",0);
153 
154  _t0 = p.get<double>("G4Time");
155  _t0_sigma = p.get<double>("G4TimeJitter");
156  if(_t0_sigma < 0) this->abort("Cannot have a negative value for G4 time jitter");
157 
158  _multi_min = p.get<size_t>("MultiMin");
159  _multi_max = p.get<size_t>("MultiMax");
160  _cosmic_distribution = p.get<bool>("CosmicDistribution", false);
161 
162  _tpc_v = p.get<std::vector<std::vector<unsigned short> > >("TPCRange");
163  auto const xrange = p.get<std::vector<double> > ("XRange");
164  auto const yrange = p.get<std::vector<double> > ("YRange");
165  auto const zrange = p.get<std::vector<double> > ("ZRange");
166  auto const part_cfg = p.get<fhicl::ParameterSet>("ParticleParameter");
167 
168  _param_v.clear();
169  auto const pdg_v = part_cfg.get<std::vector<std::vector<int> > > ("PDGCode");
170  auto const minmult_v = part_cfg.get<std::vector<unsigned short> > ("MinMulti");
171  auto const maxmult_v = part_cfg.get<std::vector<unsigned short> > ("MaxMulti");
172  auto const weight_v = part_cfg.get<std::vector<double> > ("ProbWeight");
173 
174  auto kerange_v = part_cfg.get<std::vector<std::vector<double> > > ("KERange");
175  auto momrange_v = part_cfg.get<std::vector<std::vector<double> > > ("MomRange");
176 
177  if( (kerange_v.empty() && momrange_v.empty()) ||
178  (!kerange_v.empty() && !momrange_v.empty()) ) {
179  this->abort("Only one of KERange or MomRange must be empty!");
180  }
181 
182  bool use_mom = false;
183  if(kerange_v.empty()){
184  kerange_v = momrange_v;
185  use_mom = true;
186  }
187  // sanity check
188  if( pdg_v.size() != kerange_v.size() ||
189  pdg_v.size() != minmult_v.size() ||
190  pdg_v.size() != maxmult_v.size() ||
191  pdg_v.size() != weight_v.size() )
192  this->abort("configuration parameters have incompatible lengths!");
193 
194  // further sanity check (1 more depth for some double-array)
195  for(auto const& r : pdg_v ) { if( r.empty() ) this->abort("PDG code not given!"); }
196  for(auto const& r : kerange_v) { if( r.size()!=2) this->abort("Incompatible legnth @ KE vector!"); }
197 
198  for(size_t idx=0; idx<minmult_v.size(); ++idx) {
199  if(minmult_v[idx] > maxmult_v[idx]) this->abort("Particle MinMulti > Particle MaxMulti!");
200  if(minmult_v[idx] > _multi_max) this->abort("Particle MinMulti > overall MultiMax!");
201  if(minmult_v[idx] > _multi_min)
202  _multi_min = minmult_v[idx];
203  }
204  if(_multi_max < _multi_min) this->abort("Overall MultiMax <= overall MultiMin!");
205 
206  if(!xrange.empty() && xrange.size() >2) this->abort("Incompatible legnth @ X vector!" );
207  if(!yrange.empty() && yrange.size() >2) this->abort("Incompatible legnth @ Y vector!" );
208  if(!zrange.empty() && zrange.size() >2) this->abort("Incompatible legnth @ Z vector!" );
209 
210  // slight modification from mpv: define the overall volume across specified TPC IDs + range options
211  double xmin,xmax,ymin,ymax,zmin,zmax;
212  xmin = ymin = zmin = 1.e20;
213  xmax = ymax = zmax = -1.e20;
214  // Implementation of required member function here.
215  auto geop = lar::providerFrom<geo::Geometry>();
216  for(auto const& tpc_id : _tpc_v) {
217  assert(tpc_id.size() == 2);
218  size_t cid = tpc_id[0];
219  size_t tid = tpc_id[1];
220  auto const& cryostat = geop->Cryostat(cid);
221  assert(cryostat.HasTPC(tid));
222 
223  auto const& tpc = cryostat.TPC(tid);
224  auto const& tpcabox = tpc.ActiveBoundingBox();
225  xmin = std::min(tpcabox.MinX(), xmin);
226  ymin = std::min(tpcabox.MinY(), ymin);
227  zmin = std::min(tpcabox.MinZ(), zmin);
228  xmax = std::max(tpcabox.MaxX(), xmax);
229  ymax = std::max(tpcabox.MaxY(), ymax);
230  zmax = std::max(tpcabox.MaxZ(), zmax);
231 
232  if(_debug) {
233  std::cout << "Using Cryostat " << tpc_id[0] << " TPC " << tpc_id[1]
234  << " ... X " << xmin << " => " << xmax
235  << " ... Y " << ymin << " => " << ymax
236  << " ... Z " << zmin << " => " << zmax
237  << std::endl;
238  }
239  }
240 
241  // range register
242  if(xrange.size()==1) { _xrange[0] = _xrange[1] = xrange[0]; }
243  if(yrange.size()==1) { _yrange[0] = _yrange[1] = yrange[0]; }
244  if(zrange.size()==1) { _zrange[0] = _zrange[1] = zrange[0]; }
245  if(xrange.size()==2) { _xrange[0] = xrange[0]; _xrange[1] = xrange[1]; }
246  if(yrange.size()==2) { _yrange[0] = yrange[0]; _yrange[1] = yrange[1]; }
247  if(zrange.size()==2) { _zrange[0] = zrange[0]; _zrange[1] = zrange[1]; }
248 
249  _xrange[0] = xmin + _xrange[0];
250  _xrange[1] = xmax - _xrange[1];
251  _yrange[0] = ymin + _yrange[0];
252  _yrange[1] = ymax - _yrange[1];
253  _zrange[0] = zmin + _zrange[0];
254  _zrange[1] = zmax - _zrange[1];
255 
256  // check
257  assert(_xrange[0] <= _xrange[1]);
258  assert(_yrange[0] <= _yrange[1]);
259  assert(_zrange[0] <= _zrange[1]);
260 
261  if(_debug>0) {
262  std::cout<<"Particle generation world boundaries..."<<std::endl
263  <<"X " << _xrange[0] << " => " << _xrange[1] << std::endl
264  <<"Y " << _yrange[0] << " => " << _yrange[1] << std::endl
265  <<"Z " << _zrange[0] << " => " << _zrange[1] << std::endl;
266  }
267 
268 
269  // register
270  //auto db = new TDatabasePDG;
271  auto db = TDatabasePDG::Instance();
272  bool direct_inward = p.get<bool>("DirectInward", true);
273  if (_cosmic_distribution && direct_inward) {
274  std::cout << "Cannot satisfy both CosmicDistribution and Inward Directioning at the same time" << std::endl;
275  throw std::exception();
276  }
277  for(size_t idx=0; idx<pdg_v.size(); ++idx) {
278  auto const& pdg = pdg_v[idx];
279  auto const& kerange = kerange_v[idx];
280  PartGenParam param;
281  param.direct_inward = direct_inward;
282  param.use_mom = use_mom;
283  param.pdg = pdg;
284  param.kerange[0] = kerange[0];
285  param.kerange[1] = kerange[1];
286  param.mass.resize(pdg.size());
287  param.multi[0] = minmult_v[idx];
288  param.multi[1] = maxmult_v[idx];
289  param.weight = weight_v[idx];
290  for(size_t i=0; i<pdg.size(); ++i)
291  param.mass[i] = db->GetParticle(param.pdg[i])->Mass();
292 
293  // sanity check
294  if(kerange[0]<0 || kerange[1]<0)
295  this->abort("You provided negative energy? Fuck off Mr. Trump.");
296 
297  // overall range check
298  if(param.kerange[0] > param.kerange[1]) this->abort("KE range has no phase space...");
299 
300  if(_debug>0) {
301  std::cout << "Generating particle (PDG";
302  for(auto const& pdg : param.pdg) std::cout << " " << pdg;
303  std::cout << ")" << std::endl
304  << (param.use_mom ? " KE range ....... " : " Mom range ....... ")
305  << param.kerange[0] << " => " << param.kerange[1] << " MeV" << std::endl
306  << std::endl;
307  }
308 
309  _param_v.push_back(param);
310  }
311 }
312 
313 void MultiPartRain::beginRun(art::Run& run)
314 {
315  // grab the geometry object to see what geometry we are using
316  art::ServiceHandle<geo::Geometry> geo;
317 
318  std::unique_ptr<sumdata::RunData> runData(new sumdata::RunData(geo->DetectorName()));
319 
320  run.put(std::move(runData));
321 
322  return;
323 }
324 
325 std::vector<size_t> MultiPartRain::GenParticles() const {
326 
327  std::vector<size_t> result;
328  std::vector<size_t> gen_count_v(_param_v.size(),0);
329  std::vector<double> weight_v(_param_v.size(),0);
330  for(size_t idx=0; idx<_param_v.size(); ++idx)
331  weight_v[idx] = _param_v[idx].weight;
332 
333  int num_part = (int)(fFlatRandom->fire(_multi_min,_multi_max+1-1.e-10));
334 
335  while(num_part) {
336 
337  double total_weight = 0;
338  for(auto const& v : weight_v) total_weight += v;
339 
340  double rval = 0;
341  rval = fFlatRandom->fire(0,total_weight);
342 
343  size_t idx = 0;
344  for(idx = 0; idx < weight_v.size(); ++idx) {
345  rval -= weight_v[idx];
346  if(rval <=0.) break;
347  }
348 
349  // register to the output
350  result.push_back(idx);
351 
352  // if generation count exceeds max, set probability weight to be 0
353  gen_count_v[idx] += 1;
354  if(gen_count_v[idx] >= _param_v[idx].multi[1])
355  weight_v[idx] = 0.;
356 
357  --num_part;
358  }
359  return result;
360 }
361 
362 void MultiPartRain::GenPosition(double& x, double& y, double& z) {
363 
364  x = fFlatRandom->fire(_xrange[0],_xrange[1]);
365  y = fFlatRandom->fire(_yrange[0],_yrange[1]);
366  z = fFlatRandom->fire(_zrange[0],_zrange[1]);
367 
368  if(_debug>0) {
369  std::cout << "Generating a rain particle at ("
370  << x << "," << y << "," << z << ")" << std::endl;
371  }
372 }
373 
374 std::array<double, 3U> MultiPartRain::extractDirection() const {
375  double px, py, pz;
376  if (_cosmic_distribution) {
377  double phi = fFlatRandom->fire(0, 2 * util::pi());
378  // Zenith Angle Theta is in [pi/2, pi]
379  double costheta = - 1. * fCosmicAngleRandom->fire();
380  double sintheta = sqrt(1 - costheta * costheta);
381  pz = cos(phi) * sintheta;
382  px = sin(phi) * sintheta;
383  py = costheta;
384  } else {
385  px = fNormalRandom->fire(0, 1);
386  py = fNormalRandom->fire(0, 1);
387  pz = fNormalRandom->fire(0, 1);
388  double p = std::hypot(px, py, pz);
389  px = px / p;
390  py = py / p;
391  pz = pz / p;
392  }
393 
394  std::array<double, 3U> result = {px, py, pz};
395  return result;
396 }
397 
398 void MultiPartRain::GenMomentum(const PartGenParam& param, const double& mass, double& px, double& py, double& pz,
399  const double x, const double y, const double z) {
400 
401  double tot_energy = 0;
402  if(param.use_mom)
403  tot_energy = sqrt(cet::square(fFlatRandom->fire(param.kerange[0],param.kerange[1])) + cet::square(mass));
404  else
405  tot_energy = fFlatRandom->fire(param.kerange[0],param.kerange[1]) + mass;
406 
407  double mom_mag = sqrt(cet::square(tot_energy) - cet::square(mass));
408 
409  /* Generating unit vector with uniform distribution
410  * in direction = over the sphere.
411  *
412  * It is sufficient to draw a normal variable in
413  * each direction and normalize.
414  *
415  * https://mathworld.wolfram.com/SpherePointPicking.html
416  */
417 
418  if(!param.direct_inward) {
419  std::cout<<"No inward directioning..."<<std::endl;
420  std::array<double, 3U> p = extractDirection();
421  px = p[0]; py = p[1]; pz = p[2];
422  }else{
423  double sign_x = ( (x - _xrange[0]) < (_xrange[1] - x) ? 1.0 : -1.0 );
424  double sign_y = ( (y - _yrange[0]) < (_yrange[1] - y) ? 1.0 : -1.0 );
425  double sign_z = ( (z - _zrange[0]) < (_zrange[1] - z) ? 1.0 : -1.0 );
426 
427  if(_debug) {
428  std::cout<<"Generating XYZ direction sign for a particle at (" << x << "," << y << "," << z << ")" << std::endl
429  <<"X: " << _xrange[0] << " => " << _xrange[1] << " ... " << sign_x << std::endl
430  <<"Y: " << _yrange[0] << " => " << _yrange[1] << " ... " << sign_y << std::endl
431  <<"Z: " << _zrange[0] << " => " << _zrange[1] << " ... " << sign_z << std::endl
432  << std::endl;
433  }
434 
435  while(1) {
436  std::array<double, 3U> p = extractDirection();
437  px = p[0]; py = p[1]; pz = p[2];
438  if( (px * sign_x) >= 0. &&
439  (py * sign_y) >= 0. &&
440  (pz * sign_z) >= 0. )
441  break;
442  }
443 
444  }
445  //std::cout<<"LOGME,"<<phi<<","<<theta<<","<<px<<","<<py<<","<<pz<<std::endl;
446 
447  if(_debug>1)
448  std::cout << " Direction : (" << px << "," << py << "," << pz << ")" << std::endl
449  << " Momentum : " << mom_mag << " [MeV/c]" << std::endl
450  << " Energy : " << tot_energy << " [MeV/c^2]" << std::endl;
451  px *= mom_mag;
452  py *= mom_mag;
453  pz *= mom_mag;
454 
455 }
456 
457 void MultiPartRain::produce(art::Event & e)
458 {
459  if(_debug>0) std::cout << "Processing a new event..." << std::endl;
460 
461  std::unique_ptr< std::vector<simb::MCTruth> > mctArray(new std::vector<simb::MCTruth>);
462 
463  simb::MCTruth mct;
464 
465  mct.SetOrigin(simb::kCosmicRay);
466 
467  std::vector<simb::MCParticle> part_v;
468 
469  auto const param_idx_v = GenParticles();
470  if(_debug)
471  std::cout << "Generating" << param_idx_v.size() << " particles..." << std::endl;
472 
473  for(size_t idx=0; idx<param_idx_v.size(); ++idx) {
474  auto const& param = _param_v[param_idx_v[idx]];
475  double px,py,pz;
476  // decide which particle
477  size_t pdg_index = (size_t)(fFlatRandom->fire(0,param.pdg.size()-1.e-10));
478  auto const& pdg = param.pdg[pdg_index];
479  auto const& mass = param.mass[pdg_index];
480  if(_debug) std::cout << " " << idx << "th instance PDG " << pdg << std::endl;
481  double x, y, z;
482  GenPosition(x,y,z);
483  double g4_time = fFlatRandom->fire(_t0 - _t0_sigma/2., _t0 + _t0_sigma/2.);
484  TLorentzVector pos(x,y,z,g4_time);
485  std::cout<<"Generating momentum..."<<std::endl;
486  GenMomentum(param,mass,px,py,pz,x,y,z);
487  std::cout<<"done"<<std::endl;
488  TLorentzVector mom(px,py,pz,sqrt(cet::square(px)+cet::square(py)+cet::square(pz)+cet::square(mass)));
489  simb::MCParticle part(part_v.size(), pdg, "primary", 0, mass, 1);
490  part.AddTrajectoryPoint(pos,mom);
491  part_v.emplace_back(std::move(part));
492  }
493 
494  if(_debug) std::cout << "Total number particles: " << mct.NParticles() << std::endl;
495  /*
496  simb::MCParticle nu(mct.NParticles(), 16, "primary", mct.NParticles(), 0, 0);
497  double px=0;
498  double py=0;
499  double pz=0;
500  double en=0;
501  for(auto const& part : part_v) {
502  px = part.Momentum().Px();
503  py = part.Momentum().Py();
504  pz = part.Momentum().Pz();
505  en = part.Momentum().E();
506  }
507  TLorentzVector mom(px,py,pz,en);
508  nu.AddTrajectoryPoint(pos,mom);
509 
510  mct.Add(nu);
511  */
512  for(auto& part : part_v)
513  mct.Add(part);
514 
515  mctArray->push_back(mct);
516 
517  e.put(std::move(mctArray));
518 }
519 
520 DEFINE_ART_MODULE(MultiPartRain)
process_name opflash particleana ie ie ie z
Utilities related to art service access.
var pdg
Definition: selectors.fcl:14
process_name opflash particleana ie x
BEGIN_PROLOG could also be cerr
BEGIN_PROLOG pz
Definition: filemuons.fcl:10
std::vector< int > pdg
pdgs p
Definition: selectors.fcl:22
CLHEP::HepRandomEngine & fFlatEngine
std::array< double, 2 > _xrange
process_name pandoraGausCryo1 vertexChargeCryo1 vertexStubCryo1 xmin
process_name opflash particleana ie ie y
std::array< double, 3U > extractDirection() const
MultiPartRain & operator=(MultiPartRain const &)=delete
void produce(art::Event &e) override
const Cut kCosmicRay
void GenPosition(double &x, double &y, double &z)
createEngine this
std::array< double, 2 > _zrange
std::vector< PartGenParam > _param_v
void GenMomentum(const PartGenParam &param, const double &mass, double &px, double &py, double &pz, const double x, const double y, const double z)
std::unique_ptr< CLHEP::RandFlat > fFlatRandom
void beginRun(art::Run &run) override
BEGIN_PROLOG px
Definition: filemuons.fcl:10
std::array< double, 2 > _yrange
process_name physics producers generator physics producers generator physics producers generator py
MultiPartRain(fhicl::ParameterSet const &p)
float mass
Definition: dedx.py:47
std::vector< double > mass
constexpr T pi()
Returns the constant pi (up to 35 decimal digits of precision)
do i e
std::array< double, 2 > kerange
std::unique_ptr< CLHEP::RandGeneral > fCosmicAngleRandom
Collection of Physical constants used in LArSoft.
height to which particles are projected pnfs larsoft persistent physics cosmics Fermilab CORSIKA standard He_showers_ * db
std::unique_ptr< CLHEP::RandGauss > fNormalRandom
esac echo uname r
std::array< size_t, 2 > multi
unsigned short _debug
void abort(const std::string msg) const
std::vector< size_t > GenParticles() const
art framework interface to geometry description
BEGIN_PROLOG could also be cout
std::vector< std::vector< unsigned short > > _tpc_v