LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
CORSIKAGen_module.cc
Go to the documentation of this file.
1 
8 // ROOT includes
9 #include "TDatabasePDG.h"
10 #include "TString.h"
11 #include "TSystem.h" //need BaseName and DirName
12 
13 // Framework includes
19 #include "fhiclcpp/ParameterSet.h"
21 
22 // art extensions
24 
25 // larsoft includes
30 
31 #include "CLHEP/Random/RandFlat.h"
32 #include "CLHEP/Random/RandPoissonQ.h"
33 #include "ifdh.h" //to handle flux files
34 #include <sqlite3.h>
35 
36 namespace evgen {
37 
221  class CORSIKAGen : public art::EDProducer {
222  public:
223  explicit CORSIKAGen(fhicl::ParameterSet const& pset);
224  virtual ~CORSIKAGen();
225 
226  void produce(art::Event& evt);
227  void beginRun(art::Run& run);
228 
229  private:
230  void openDBs(std::string const& module_label);
231  void populateNShowers();
232  void populateTOffset();
233  void GetSample(simb::MCTruth&);
234  double wrapvar(const double var, const double low, const double high);
235  double wrapvarBoxNo(const double var, const double low, const double high, int& boxno);
255  void ProjectToBoxEdge(const double xyz[],
256  const double dxyz[],
257  const double xlo,
258  const double xhi,
259  const double ylo,
260  const double yhi,
261  const double zlo,
262  const double zhi,
263  double xyzout[]);
264 
265  int fShowerInputs = 0;
266  std::vector<double>
268  std::vector<int> fMaxShowers; //< Max number of showers to query, one per showerinput
269  double fShowerBounds[6] = {
270  0.,
271  0.,
272  0.,
273  0.,
274  0.,
275  0.};
277  0.;
278  ifdh_ns::ifdh* fIFDH = 0;
279 
280  //fcl parameters
281  double fProjectToHeight = 0.;
282  std::vector<std::string> fShowerInputFiles;
283  std::string fShowerCopyType; // ifdh file selection and copying (default) or direct file path
284  std::vector<double>
286  double fSampleTime = 0.;
287  double fToffset = 0.;
288  std::vector<double>
291  0.;
292  sqlite3* fdb[5];
293  double fRandomXZShift =
294  0.;
295  CLHEP::HepRandomEngine& fGenEngine;
296  CLHEP::HepRandomEngine& fPoisEngine;
297  };
298 }
299 
300 namespace evgen {
301 
303  : EDProducer{p}
304  , fProjectToHeight(p.get<double>("ProjectToHeight", 0.))
305  , fShowerInputFiles(p.get<std::vector<std::string>>("ShowerInputFiles"))
306  , fShowerCopyType(p.get<std::string>("ShowerCopyType", "IFDH"))
307  , fShowerFluxConstants(p.get<std::vector<double>>("ShowerFluxConstants"))
308  , fSampleTime(p.get<double>("SampleTime", 0.))
309  , fToffset(p.get<double>("TimeOffset", 0.))
310  , fBuffBox(p.get<std::vector<double>>("BufferBox", {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}))
311  , fShowerAreaExtension(p.get<double>("ShowerAreaExtension", 0.))
312  , fRandomXZShift(p.get<double>("RandomXZShift", 0.))
313  , fGenEngine(art::ServiceHandle<rndm::NuRandomService>()->registerAndSeedEngine(
314  createEngine(0, "HepJamesRandom", "gen"),
315  "HepJamesRandom",
316  "gen",
317  p,
318  {"Seed", "SeedGenerator"}))
319  , fPoisEngine(art::ServiceHandle<rndm::NuRandomService>()->registerAndSeedEngine(
320  createEngine(0, "HepJamesRandom", "pois"),
321  "HepJamesRandom",
322  "pois",
323  p,
324  "SeedPoisson"))
325  {
326  if (fShowerInputFiles.size() != fShowerFluxConstants.size() || fShowerInputFiles.size() == 0 ||
327  fShowerFluxConstants.size() == 0)
328  throw cet::exception("CORSIKAGen")
329  << "ShowerInputFiles and ShowerFluxConstants have different or invalid sizes!"
330  << "\n";
332 
333  if (fSampleTime == 0.) throw cet::exception("CORSIKAGen") << "SampleTime not set!";
334 
335  if (fProjectToHeight == 0.) mf::LogInfo("CORSIKAGen") << "Using 0. for fProjectToHeight!";
336  // create a default random engine; obtain the random seed from NuRandomService,
337  // unless overridden in configuration with key "Seed"
338 
339  this->openDBs(p.get<std::string>("module_label"));
340  this->populateNShowers();
341  this->populateTOffset();
342 
343  produces<std::vector<simb::MCTruth>>();
344  produces<sumdata::RunData, art::InRun>();
345  }
346 
348  {
349  for (int i = 0; i < fShowerInputs; i++) {
350  sqlite3_close(fdb[i]);
351  }
352  //cleanup temp files
353  fIFDH->cleanup();
354  }
355 
356  void CORSIKAGen::ProjectToBoxEdge(const double xyz[],
357  const double indxyz[],
358  const double xlo,
359  const double xhi,
360  const double ylo,
361  const double yhi,
362  const double zlo,
363  const double zhi,
364  double xyzout[])
365  {
366 
367  //we want to project backwards, so take mirror of momentum
368  const double dxyz[3] = {-indxyz[0], -indxyz[1], -indxyz[2]};
369 
370  // Compute the distances to the x/y/z walls
371  double dx = 99.E99;
372  double dy = 99.E99;
373  double dz = 99.E99;
374  if (dxyz[0] > 0.0) { dx = (xhi - xyz[0]) / dxyz[0]; }
375  else if (dxyz[0] < 0.0) {
376  dx = (xlo - xyz[0]) / dxyz[0];
377  }
378  if (dxyz[1] > 0.0) { dy = (yhi - xyz[1]) / dxyz[1]; }
379  else if (dxyz[1] < 0.0) {
380  dy = (ylo - xyz[1]) / dxyz[1];
381  }
382  if (dxyz[2] > 0.0) { dz = (zhi - xyz[2]) / dxyz[2]; }
383  else if (dxyz[2] < 0.0) {
384  dz = (zlo - xyz[2]) / dxyz[2];
385  }
386 
387  // Choose the shortest distance
388  double d = 0.0;
389  if (dx < dy && dx < dz)
390  d = dx;
391  else if (dy < dz && dy < dx)
392  d = dy;
393  else if (dz < dx && dz < dy)
394  d = dz;
395 
396  // Make the step
397  for (int i = 0; i < 3; ++i) {
398  xyzout[i] = xyz[i] + dxyz[i] * d;
399  }
400  }
401 
402  void CORSIKAGen::openDBs(std::string const& module_label)
403  {
404  //choose files based on fShowerInputFiles, copy them with ifdh, open them
405  // for c2: statement is unused
406  //sqlite3_stmt *statement;
407  CLHEP::RandFlat flat(fGenEngine);
408 
409  //setup ifdh object
410  if (!fIFDH) fIFDH = new ifdh_ns::ifdh;
411  const char* ifdh_debug_env = std::getenv("IFDH_DEBUG_LEVEL");
412  if (ifdh_debug_env) {
413  mf::LogInfo("CORSIKAGen") << "IFDH_DEBUG_LEVEL: " << ifdh_debug_env << "\n";
414  fIFDH->set_debug(ifdh_debug_env);
415  }
416 
417  //get ifdh path for each file in fShowerInputFiles, put into selectedflist
418  //if 1 file returned, use that file
419  //if >1 file returned, randomly select one file
420  //if 0 returned, make exeption for missing files
421  std::vector<std::pair<std::string, long>> selectedflist;
422  for (int i = 0; i < fShowerInputs; i++) {
423  if (fShowerInputFiles[i].find("*") == std::string::npos) {
424  //if there are no wildcards, don't call findMatchingFiles
425  selectedflist.push_back(std::make_pair(fShowerInputFiles[i], 0));
426  mf::LogInfo("CorsikaGen") << "Selected" << selectedflist.back().first << "\n";
427  }
428  else {
429  //use findMatchingFiles
430  //default to IFDH approach when wildcards in path
431  std::vector<std::pair<std::string, long>> flist;
432  std::string path(gSystem->DirName(fShowerInputFiles[i].c_str()));
433  std::string pattern(gSystem->BaseName(fShowerInputFiles[i].c_str()));
434  flist = fIFDH->findMatchingFiles(path, pattern);
435  unsigned int selIndex = -1;
436  if (flist.size() == 1) { //0th element is the search path:pattern
437  selIndex = 0;
438  }
439  else if (flist.size() > 1) {
440  selIndex = (unsigned int)(flat() * (flist.size() - 1) +
441  0.5); //rnd with rounding, dont allow picking the 0th element
442  }
443  else {
444  throw cet::exception("CORSIKAGen")
445  << "No files returned for path:pattern: " << path << ":" << pattern << std::endl;
446  }
447  selectedflist.push_back(flist[selIndex]);
448  mf::LogInfo("CorsikaGen") << "For " << fShowerInputFiles[i] << ":" << pattern << "\nFound "
449  << flist.size() << " candidate files"
450  << "\nChoosing file number " << selIndex << "\n"
451  << "\nSelected " << selectedflist.back().first << "\n";
452  }
453  }
454 
455  //do the fetching, store local filepaths in locallist
456  std::vector<std::string> locallist;
457  for (unsigned int i = 0; i < selectedflist.size(); i++) {
458  mf::LogInfo("CorsikaGen") << "Fetching: " << selectedflist[i].first << " "
459  << selectedflist[i].second << "\n";
460  if (fShowerCopyType == "IFDH") {
461  std::string fetchedfile(fIFDH->fetchInput(selectedflist[i].first));
462  MF_LOG_DEBUG("CorsikaGen") << "Fetched; local path: " << fetchedfile << "; copy type: IFDH";
463  locallist.push_back(fetchedfile);
464  }
465  else if (fShowerCopyType == "DIRECT") {
466  std::string fetchedfile(selectedflist[i].first);
467  MF_LOG_DEBUG("CorsikaGen")
468  << "Fetched; local path: " << fetchedfile << "; copy type: DIRECT";
469  locallist.push_back(fetchedfile);
470  }
471  else
472  throw cet::exception("CORSIKAGen")
473  << "Error copying shower db: ShowerCopyType must be \"IFDH\" or \"DIRECT\"\n";
474  }
475 
476  //open the files in fShowerInputFilesLocalPaths with sqlite3
477  for (unsigned int i = 0; i < locallist.size(); i++) {
478  //prepare and execute statement to attach db file
479  int res = sqlite3_open(locallist[i].c_str(), &fdb[i]);
480  if (res != SQLITE_OK)
481  throw cet::exception("CORSIKAGen")
482  << "Error opening db: (" << locallist[i].c_str() << ") (" << res
483  << "): " << sqlite3_errmsg(fdb[i]) << "; memory used:<<" << sqlite3_memory_used() << "/"
484  << sqlite3_memory_highwater(0) << "\n";
485  else
486  mf::LogInfo("CORSIKAGen") << "Attached db " << locallist[i] << "\n";
487  }
488  }
489 
490  double CORSIKAGen::wrapvar(const double var, const double low, const double high)
491  {
492  //wrap variable so that it's always between low and high
493  return (var - (high - low) * floor(var / (high - low))) + low;
494  }
495 
496  double CORSIKAGen::wrapvarBoxNo(const double var, const double low, const double high, int& boxno)
497  {
498  //wrap variable so that it's always between low and high
499  boxno = int(floor(var / (high - low)));
500  return (var - (high - low) * floor(var / (high - low))) + low;
501  }
502 
504  {
505  //populate TOffset_corsika by finding minimum ParticleTime from db file
506 
507  sqlite3_stmt* statement;
508  const std::string kStatement("select min(t) from particles");
509  double t = 0.;
510 
511  for (int i = 0; i < fShowerInputs; i++) {
512  //build and do query to get run min(t) from each db
513  if (sqlite3_prepare(fdb[i], kStatement.c_str(), -1, &statement, 0) == SQLITE_OK) {
514  int res = 0;
515  res = sqlite3_step(statement);
516  if (res == SQLITE_ROW) {
517  t = sqlite3_column_double(statement, 0);
518  mf::LogInfo("CORSIKAGen")
519  << "For showers input " << i << " found particles.min(t)=" << t << "\n";
520  if (i == 0 || t < fToffset_corsika) fToffset_corsika = t;
521  }
522  else {
523  throw cet::exception("CORSIKAGen")
524  << "Unexpected sqlite3_step return value: (" << res << "); "
525  << "ERROR:" << sqlite3_errmsg(fdb[i]) << "\n";
526  }
527  }
528  else {
529  throw cet::exception("CORSIKAGen") << "Error preparing statement: (" << kStatement << "); "
530  << "ERROR:" << sqlite3_errmsg(fdb[i]) << "\n";
531  }
532  }
533 
534  mf::LogInfo("CORSIKAGen") << "Found corsika timeoffset [ns]: " << fToffset_corsika << "\n";
535  }
536 
538  {
539  //populate vector of the number of showers per event based on:
540  //AREA the showers are being distributed over
541  //TIME of the event (fSampleTime)
542  //flux constants that determine the overall normalizations (fShowerFluxConstants)
543  //Energy range over which the sample was generated (ERANGE_*)
544  //power spectrum over which the sample was generated (ESLOPE)
545 
546  //compute shower area based on the maximal x,z dimensions of cryostat boundaries + fShowerAreaExtension
548  for (auto const& cryostat : geom->Iterate<geo::CryostatGeo>()) {
549  double bounds[6] = {0.};
550  cryostat.Boundaries(bounds);
551  for (unsigned int bnd = 0; bnd < 6; bnd++) {
552  mf::LogVerbatim("CORSIKAGen") << "Cryo Boundary: " << bnd << "=" << bounds[bnd]
553  << " ( + Buffer=" << fBuffBox[bnd] << ")\n";
554  if (fabs(bounds[bnd]) > fabs(fShowerBounds[bnd])) { fShowerBounds[bnd] = bounds[bnd]; }
555  }
556  }
557  //add on fShowerAreaExtension without being clever
562 
563  double showersArea = (fShowerBounds[1] / 100 - fShowerBounds[0] / 100) *
564  (fShowerBounds[5] / 100 - fShowerBounds[4] / 100);
565 
566  mf::LogInfo("CORSIKAGen") << "Area extended by : " << fShowerAreaExtension
567  << "\nShowers to be distributed betweeen: x=" << fShowerBounds[0]
568  << "," << fShowerBounds[1] << " & z=" << fShowerBounds[4] << ","
569  << fShowerBounds[5]
570  << "\nShowers to be distributed with random XZ shift: "
571  << fRandomXZShift << " cm"
572  << "\nShowers to be distributed over area: " << showersArea << " m^2"
573  << "\nShowers to be distributed over time: " << fSampleTime << " s"
574  << "\nShowers to be distributed with time offset: " << fToffset
575  << " s"
576  << "\nShowers to be distributed at y: " << fShowerBounds[3] << " cm";
577 
578  //db variables
579  sqlite3_stmt* statement;
580  const std::string kStatement("select erange_high,erange_low,eslope,nshow from input");
581  double upperLimitOfEnergyRange = 0., lowerLimitOfEnergyRange = 0., energySlope = 0.,
582  oneMinusGamma = 0., EiToOneMinusGamma = 0., EfToOneMinusGamma = 0.;
583 
584  for (int i = 0; i < fShowerInputs; i++) {
585  //build and do query to get run info from databases
586  // double thisrnd=flat();//need a new random number for each query
587  if (sqlite3_prepare(fdb[i], kStatement.c_str(), -1, &statement, 0) == SQLITE_OK) {
588  int res = 0;
589  res = sqlite3_step(statement);
590  if (res == SQLITE_ROW) {
591  upperLimitOfEnergyRange = sqlite3_column_double(statement, 0);
592  lowerLimitOfEnergyRange = sqlite3_column_double(statement, 1);
593  energySlope = sqlite3_column_double(statement, 2);
594  fMaxShowers.push_back(sqlite3_column_int(statement, 3));
595  oneMinusGamma = 1 + energySlope;
596  EiToOneMinusGamma = pow(lowerLimitOfEnergyRange, oneMinusGamma);
597  EfToOneMinusGamma = pow(upperLimitOfEnergyRange, oneMinusGamma);
598  mf::LogVerbatim("CORSIKAGen")
599  << "For showers input " << i << " found e_hi=" << upperLimitOfEnergyRange
600  << ", e_lo=" << lowerLimitOfEnergyRange << ", slope=" << energySlope
601  << ", k=" << fShowerFluxConstants[i] << "\n";
602  }
603  else {
604  throw cet::exception("CORSIKAGen")
605  << "Unexpected sqlite3_step return value: (" << res << "); "
606  << "ERROR:" << sqlite3_errmsg(fdb[i]) << "\n";
607  }
608  }
609  else {
610  throw cet::exception("CORSIKAGen") << "Error preparing statement: (" << kStatement << "); "
611  << "ERROR:" << sqlite3_errmsg(fdb[i]) << "\n";
612  }
613 
614  //this is computed, how?
615  double NShowers = (M_PI * showersArea * fShowerFluxConstants[i] *
616  (EfToOneMinusGamma - EiToOneMinusGamma) / oneMinusGamma) *
617  fSampleTime;
618  fNShowersPerEvent.push_back(NShowers);
619  mf::LogVerbatim("CORSIKAGen")
620  << "For showers input " << i << " the number of showers per event is " << (int)NShowers
621  << "\n";
622  }
623  }
624 
626  {
627  //for each input, randomly pull fNShowersPerEvent[i] showers from the Particles table
628  //and randomly place them in time (between -fSampleTime/2 and fSampleTime/2)
629  //wrap their positions based on the size of the area under consideration
630  //based on http://nusoft.fnal.gov/larsoft/doxsvn/html/CRYHelper_8cxx_source.html (Sample)
631 
632  //query from sqlite db with select * from particles where shower in (select id from showers ORDER BY substr(id*0.51123124141,length(id)+2) limit 100000) ORDER BY substr(shower*0.51123124141,length(shower)+2);
633  //where 0.51123124141 is a random seed to allow randomly selecting rows and should be randomly generated for each query
634  //the inner order by is to select randomly from the possible shower id's
635  //the outer order by is to make sure the shower numbers are ordered randomly (without this, the showers always come out ordered by shower number
636  //and 100000 is the number of showers to be selected at random and needs to be less than the number of showers in the showers table
637 
638  //TDatabasePDG is for looking up particle masses
639  static TDatabasePDG* pdgt = TDatabasePDG::Instance();
640 
641  //db variables
642  sqlite3_stmt* statement;
643  const TString kStatement(
644  "select shower,pdg,px,py,pz,x,z,t,e from particles where shower in (select id from showers "
645  "ORDER BY substr(id*%f,length(id)+2) limit %d) ORDER BY substr(shower*%f,length(shower)+2)");
646 
647  CLHEP::RandFlat flat(fGenEngine);
648  CLHEP::RandPoissonQ randpois(fPoisEngine);
649 
650  // get geometry and figure where to project particles to, based on CRYHelper
652  double x1, x2;
653  double y1, y2;
654  double z1, z2;
655  geom->WorldBox(&x1, &x2, &y1, &y2, &z1, &z2);
656 
657  // make the world box slightly smaller so that the projection to
658  // the edge avoids possible rounding errors later on with Geant4
659  double fBoxDelta = 1.e-5;
660  x1 += fBoxDelta;
661  x2 -= fBoxDelta;
662  y1 += fBoxDelta;
663  y2 = fProjectToHeight;
664  z1 += fBoxDelta;
665  z2 -= fBoxDelta;
666 
667  //populate mctruth
668  int ntotalCtr = 0; //count number of particles added to mctruth
669  int lastShower =
670  0; //keep track of last shower id so that t can be randomized on every new shower
671  int nShowerCntr = 0; //keep track of how many showers are left to be added to mctruth
672  int nShowerQry = 0; //number of showers to query from db
673  int shower, pdg;
674  double px, py, pz, x, z, tParticleTime, etot,
675  showerTime = 0., showerTimex = 0., showerTimez = 0., showerXOffset = 0., showerZOffset = 0.,
676  t;
677  for (int i = 0; i < fShowerInputs; i++) {
678  nShowerCntr = randpois.fire(fNShowersPerEvent[i]);
679  mf::LogInfo("CORSIKAGEN") << " Shower input " << i << " with mean " << fNShowersPerEvent[i]
680  << " generating " << nShowerCntr;
681 
682  while (nShowerCntr > 0) {
683  //how many showers should we query?
684  if (nShowerCntr > fMaxShowers[i]) {
685  nShowerQry = fMaxShowers[i]; //take the group size
686  }
687  else {
688  nShowerQry = nShowerCntr; //take the rest that are needed
689  }
690  //build and do query to get nshowers
691  double thisrnd = flat(); //need a new random number for each query
692  TString kthisStatement = TString::Format(kStatement.Data(), thisrnd, nShowerQry, thisrnd);
693  MF_LOG_DEBUG("CORSIKAGen") << "Executing: " << kthisStatement;
694  if (sqlite3_prepare(fdb[i], kthisStatement.Data(), -1, &statement, 0) == SQLITE_OK) {
695  int res = 0;
696  //loop over database rows, pushing particles into mctruth object
697  while (1) {
698  res = sqlite3_step(statement);
699  if (res == SQLITE_ROW) {
700  /*
701  * Memo columns:
702  * [0] shower
703  * [1] particle ID (PDG)
704  * [2] momentum: x component [GeV/c]
705  * [3] momentum: y component [GeV/c]
706  * [4] momentum: z component [GeV/c]
707  * [5] position: x component [cm]
708  * [6] position: z component [cm]
709  * [7] time [ns]
710  * [8] energy [GeV]
711  */
712  shower = sqlite3_column_int(statement, 0);
713  if (shower != lastShower) {
714  //each new shower gets its own random time and position offsets
715  showerTime = 1e9 * (flat() * fSampleTime); //converting from s to ns
716  showerTimex = 1e9 * (flat() * fSampleTime); //converting from s to ns
717  showerTimez = 1e9 * (flat() * fSampleTime); //converting from s to ns
718  //and a random offset in both z and x controlled by the fRandomXZShift parameter
719  showerXOffset = flat() * fRandomXZShift - (fRandomXZShift / 2);
720  showerZOffset = flat() * fRandomXZShift - (fRandomXZShift / 2);
721  }
722  pdg = sqlite3_column_int(statement, 1);
723  //get mass for this particle
724  double m = 0.; // in GeV
725  TParticlePDG* pdgp = pdgt->GetParticle(pdg);
726  if (pdgp) m = pdgp->Mass();
727 
728  //Note: position/momentum in db have north=-x and west=+z, rotate so that +z is north and +x is west
729  //get momentum components
730  px = sqlite3_column_double(statement, 4); //uboone x=Particlez
731  py = sqlite3_column_double(statement, 3);
732  pz = -sqlite3_column_double(statement, 2); //uboone z=-Particlex
733  etot = sqlite3_column_double(statement, 8);
734 
735  //get/calculate position components
736  int boxnoX = 0, boxnoZ = 0;
737  x = wrapvarBoxNo(sqlite3_column_double(statement, 6) + showerXOffset,
738  fShowerBounds[0],
739  fShowerBounds[1],
740  boxnoX);
741  z = wrapvarBoxNo(-sqlite3_column_double(statement, 5) + showerZOffset,
742  fShowerBounds[4],
743  fShowerBounds[5],
744  boxnoZ);
745  tParticleTime = sqlite3_column_double(
746  statement, 7); //time offset, includes propagation time from top of atmosphere
747  //actual particle time is particle surface arrival time
748  //+ shower start time
749  //+ global offset (fcl parameter, in s)
750  //- propagation time through atmosphere
751  //+ boxNo{X,Z} time offset to make grid boxes have different shower times
752  t = tParticleTime + showerTime + (1e9 * fToffset) - fToffset_corsika +
753  showerTimex * boxnoX + showerTimez * boxnoZ;
754  //wrap surface arrival so that it's in the desired time window
755  t = wrapvar(t, (1e9 * fToffset), 1e9 * (fToffset + fSampleTime));
756 
757  simb::MCParticle p(ntotalCtr, pdg, "primary", -200, m, 1);
758 
759  //project back to wordvol/fProjectToHeight
760  /*
761  * This back propagation goes from a point on the upper surface of
762  * the cryostat back to the edge of the world, except that that
763  * world is cut short by `fProjectToHeight` (`y2`) ceiling.
764  * The projection will most often lie on that ceiling, but it may
765  * end up instead on one of the side edges of the world, or even
766  * outside it.
767  */
768  double xyzo[3];
769  double x0[3] = {x, fShowerBounds[3], z};
770  double dx[3] = {px, py, pz};
771  this->ProjectToBoxEdge(x0, dx, x1, x2, y1, y2, z1, z2, xyzo);
772 
773  TLorentzVector pos(
774  xyzo[0], xyzo[1], xyzo[2], t); // time needs to be in ns to match GENIE, etc
775  TLorentzVector mom(px, py, pz, etot);
776  p.AddTrajectoryPoint(pos, mom);
777  mctruth.Add(p);
778  ntotalCtr++;
779  lastShower = shower;
780  }
781  else if (res == SQLITE_DONE) {
782  break;
783  }
784  else {
785  throw cet::exception("CORSIKAGen")
786  << "Unexpected sqlite3_step return value: (" << res << "); "
787  << "ERROR:" << sqlite3_errmsg(fdb[i]) << "\n";
788  }
789  }
790  }
791  else {
792  throw cet::exception("CORSIKAGen")
793  << "Error preparing statement: (" << kthisStatement << "); "
794  << "ERROR:" << sqlite3_errmsg(fdb[i]) << "\n";
795  }
796  nShowerCntr = nShowerCntr - nShowerQry;
797  }
798  }
799  }
800 
802  {
804  run.put(std::make_unique<sumdata::RunData>(geo->DetectorName()), art::fullRun());
805  }
806 
808  {
809  std::unique_ptr<std::vector<simb::MCTruth>> truthcol(new std::vector<simb::MCTruth>);
810 
812 
813  simb::MCTruth truth;
815 
816  simb::MCTruth pretruth;
817  GetSample(pretruth);
818  mf::LogInfo("CORSIKAGen") << "GetSample number of particles returned: " << pretruth.NParticles()
819  << "\n";
820  // loop over particles in the truth object
821  for (int i = 0; i < pretruth.NParticles(); ++i) {
822  simb::MCParticle particle = pretruth.GetParticle(i);
823  const TLorentzVector& v4 = particle.Position();
824  const TLorentzVector& p4 = particle.Momentum();
825  double x0[3] = {v4.X(), v4.Y(), v4.Z()};
826  double dx[3] = {p4.Px(), p4.Py(), p4.Pz()};
827 
828  // now check if the particle goes through any cryostat in the detector
829  // if so, add it to the truth object.
830  for (auto const& cryostat : geom->Iterate<geo::CryostatGeo>()) {
831  double bounds[6] = {0.};
832  cryostat.Boundaries(bounds);
833 
834  //add a buffer box around the cryostat bounds to increase the acceptance and account for scattering
835  //By default, the buffer box has zero size
836  for (unsigned int cb = 0; cb < 6; cb++)
837  bounds[cb] = bounds[cb] + fBuffBox[cb];
838 
839  //calculate the intersection point with each cryostat surface
840  bool intersects_cryo = false;
841  for (int bnd = 0; bnd != 6; ++bnd) {
842  if (bnd < 2) {
843  double p2[3] = {bounds[bnd],
844  x0[1] + (dx[1] / dx[0]) * (bounds[bnd] - x0[0]),
845  x0[2] + (dx[2] / dx[0]) * (bounds[bnd] - x0[0])};
846  if (p2[1] >= bounds[2] && p2[1] <= bounds[3] && p2[2] >= bounds[4] &&
847  p2[2] <= bounds[5]) {
848  intersects_cryo = true;
849  break;
850  }
851  }
852  else if (bnd >= 2 && bnd < 4) {
853  double p2[3] = {x0[0] + (dx[0] / dx[1]) * (bounds[bnd] - x0[1]),
854  bounds[bnd],
855  x0[2] + (dx[2] / dx[1]) * (bounds[bnd] - x0[1])};
856  if (p2[0] >= bounds[0] && p2[0] <= bounds[1] && p2[2] >= bounds[4] &&
857  p2[2] <= bounds[5]) {
858  intersects_cryo = true;
859  break;
860  }
861  }
862  else if (bnd >= 4) {
863  double p2[3] = {x0[0] + (dx[0] / dx[2]) * (bounds[bnd] - x0[2]),
864  x0[1] + (dx[1] / dx[2]) * (bounds[bnd] - x0[2]),
865  bounds[bnd]};
866  if (p2[0] >= bounds[0] && p2[0] <= bounds[1] && p2[1] >= bounds[2] &&
867  p2[1] <= bounds[3]) {
868  intersects_cryo = true;
869  break;
870  }
871  }
872  }
873 
874  if (intersects_cryo) {
875  truth.Add(particle);
876  break; //leave loop over cryostats to avoid adding particle multiple times
877  } // end if particle goes into a cryostat
878  } // end loop over cryostats in the detector
879 
880  } // loop on particles
881 
882  mf::LogInfo("CORSIKAGen")
883  << "Number of particles from getsample crossing cryostat + bounding box: "
884  << truth.NParticles() << "\n";
885 
886  truthcol->push_back(truth);
887  evt.put(std::move(truthcol));
888 
889  return;
890  } // end produce
891 
892 } // end namespace
893 
894 namespace evgen {
895 
897 
898 }
Float_t x
Definition: compare.C:6
details::range_type< T > Iterate() const
Initializes the specified ID with the ID of the first cryostat.
Definition: GeometryCore.h:541
base_engine_t & createEngine(seed_t seed)
double fToffset
Time offset of sample, defaults to zero (no offset) [s].
double wrapvar(const double var, const double low, const double high)
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
const TLorentzVector & Position(const int i=0) const
Definition: MCParticle.h:220
double fSampleTime
Duration of sample [s].
void AddTrajectoryPoint(TLorentzVector const &position, TLorentzVector const &momentum)
Definition: MCParticle.h:258
constexpr auto fullRun()
double fProjectToHeight
Height to which particles will be projected [cm].
Float_t y1[n_points_granero]
Definition: compare.C:5
void SetOrigin(simb::Origin_t origin)
Definition: MCTruth.h:82
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
std::vector< std::string > fShowerInputFiles
Set of CORSIKA shower data files to use.
void WorldBox(double *xlo, double *xhi, double *ylo, double *yhi, double *zlo, double *zhi) const
Fills the arguments with the boundaries of the world.
Float_t x1[n_points_granero]
Definition: compare.C:5
EDProducer(fhicl::ParameterSet const &pset)
Definition: EDProducer.cc:6
Double_t z
Definition: plot.C:276
LArSoft interface to CORSIKA event generator.
PutHandle< PROD > put(std::unique_ptr< PROD > &&edp, std::string const &instance={})
Definition: Run.h:121
int NParticles() const
Definition: MCTruth.h:75
ifdh_ns::ifdh * fIFDH
(optional) flux file handling
Geometry information for a single cryostat.
Definition: CryostatGeo.h:43
Particle class.
Definition: Run.h:37
Float_t y2[n_points_geant4]
Definition: compare.C:26
PutHandle< PROD > put(std::unique_ptr< PROD > &&edp, std::string const &instance={})
Definition: Event.h:77
int fShowerInputs
Number of shower inputs to process from.
sqlite3 * fdb[5]
Pointers to sqlite3 database object, max of 5.
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:65
void ProjectToBoxEdge(const double xyz[], const double dxyz[], const double xlo, const double xhi, const double ylo, const double yhi, const double zlo, const double zhi, double xyzout[])
Propagates a point back to the surface of a box.
Float_t d
Definition: plot.C:235
CLHEP::HepRandomEngine & fPoisEngine
double wrapvarBoxNo(const double var, const double low, const double high, int &boxno)
std::vector< int > fMaxShowers
An art service to assist in the distribution of guaranteed unique seeds to all engines within an art ...
const simb::MCParticle & GetParticle(int i) const
Definition: MCTruth.h:76
double fToffset_corsika
Timing offset to account for propagation time through atmosphere, populated from db.
std::string fShowerCopyType
void produce(art::Event &evt)
void Add(simb::MCParticle const &part)
Definition: MCTruth.h:80
std::vector< double > fShowerFluxConstants
Set of flux constants to be associated with each shower data file.
const TLorentzVector & Momentum(const int i=0) const
Definition: MCParticle.h:221
#define MF_LOG_DEBUG(id)
double fRandomXZShift
Each shower will be shifted by a random amount in xz so that showers won&#39;t repeatedly sample the same...
double fShowerBounds[6]
Boundaries of area over which showers are to be distributed (x(min), x(max), unused, y, z(min), z(max) )
CORSIKAGen(fhicl::ParameterSet const &pset)
void beginRun(art::Run &run)
std::string const & DetectorName() const
Returns a string with the name of the detector, as configured.
Definition: GeometryCore.h:203
TCEvent evt
Definition: DataStructs.cxx:8
Event generator information.
Definition: MCTruth.h:32
Float_t x2[n_points_geant4]
Definition: compare.C:26
void GetSample(simb::MCTruth &)
std::vector< double > fNShowersPerEvent
Number of showers to put in each event of duration fSampleTime; one per showerinput.
CLHEP::HepRandomEngine & fGenEngine
Namespace collecting geometry-related classes utilities.
Event Generation using GENIE, cosmics or single particles.
void openDBs(std::string const &module_label)
double fShowerAreaExtension
Extend distribution of corsika particles in x,z by this much (e.g. 1000 will extend 10 m in -x...
art framework interface to geometry description
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
Cosmic rays.
Definition: MCTruth.h:24
std::vector< double > fBuffBox
Buffer box extensions to cryostat in each direction (6 of them: x_lo,x_hi,y_lo,y_hi,z_lo,z_hi) [cm].