MUSUN_module.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 /// \file MUSUN_module.cc
3 /// \brief Generator for underground muon propagation
4 ///
5 /// Module designed to propagate muons underground
6 ///
7 /// For a description of how to use the module see DUNE DocDB
8 /// It is highly recommended that you read it before use.....
9 ///
10 /// Original MUSUN code was written by Vitaly A. Kudryavtsev (University of Sheffield).
11 /// Conversion from Fortran to C was done by Kareem Kazkaz (LLNL) with help from David Woodward (Sheffield)
12 /// Default slant depths and surface profile work done Martin Richardson (Sheffield)
13 /// Notable contributions to surface map profiling also made by Chao Zhang (USD) and Jeff de Jong (Oxford)
14 /// Interfaced with LArSoft by Karl Warburton (University of Sheffield) with necessary changes.
15 /// ------------START OF NECCESSARY CHANGES---------------
16 /// Changing variables to fcl parameters
17 /// Restructuring to fit LArSoft design
18 /// Co-ordinate transformation... y -> x, x -> z, z -> y ????SHOULD BE x -> -z AS PER VITALY????
19 /// Restore cavern angle convention to Vitaly's definition.
20 /// ------------END OF NECCESSARY CHANGES-----------------
21 /// \author k.warburton@sheffield.ac.uk
22 ///
23 /// Further Notes - Taken from Kareem Kazkaz;
24 ///
25 /// This C++ code is a port of the musun-surf.f and test-musun-surf.f code written
26 /// by Vitaly Kudryavtsev of the University of Sheffield. It generates muons with
27 /// energy, position, and direction specific to the Davis cavern at the Sanford
28 /// Underground Research Facility in Lead, South Dakota.
29 ///
30 /// This C++ code was ported by Kareem Kazkaz, kareem@llnl.gov, (925) 422-7208
31 ///
32 /// Here are the notes from Vitaly:
33 ///
34 ///c The code samples single atmospheric muons at the SURF
35 ///c underground laboratory (Davis' cavern)
36 ///c (taking into account the slant depth distribution)
37 ///c in the energy range E1-E2, zenith angle range theta1-theta2 (0-90 degrees)
38 ///c and azimuthal angle range phi1-phi2 (0-360 degrees).
39 ///c At present only the following ranges of parameters are supported:
40 ///c E1 = 1 GeV, E2 = 10^6 GeV, theta1 = 0, theta2 = 90 deg, phi1 = 0, phi2 = 360 deg.
41 ///c
42 ///c Program uses muon energy spectra at various depths and zenith
43 ///c angles obtained with MUSIC code for muon propagation and Gaisser's
44 ///c formula for muon spectrum at sea level
45 ///c (T.K.Gaisser, Cosmic Rays and Particle Physics, Cambridge
46 ///c University Press, 1990) modified for large zenith angles and
47 ///c prompt muon flux with normalisation and spectral index
48 ///c that fit LVD data: gamma = 2.77, A = 0.14.
49 ///c Density of rock is assumed to be 2.70 g/cm^3 but can be changed
50 ///c during initialisation (previous step, ask the author).
51 ///c
52 ///c Muon flux through a sphere (Chao's coordinates) = 6.33x10^(-9) cm^(-2) s^(-1) (gamma=2.77) - old
53 ///c Muon flux through a sphere (Martin's coordinates) = 6.16x10^(-9) cm^(-2) s^(-1) (gamma=2.77) - new
54 ///c Muon flux through the cuboid (30x22x24 m^3) = 0.0588 s^(-1) (gamma=2.77)
55 ///c
56 ///c Note: the muon spectrum at sea level does not take into account
57 ///c the change of the primary spectrum slope at and above the knee
58 ///c region (3*10^15-10^16 eV).
59 ///c
60 ///c Program uses the tables of muon energy spectra at various
61 ///c zenith and azimuthal angles at SURF
62 ///c calculated with the muon propagation code MUSIC and the
63 ///c angular distribution of muon intensities at SURF (4850 ft level).
64 ///c
65 ///c Coordinate system for the muon intensities
66 ///c is determined by the mountain profile provided
67 ///c by Chao Zhang (USD, South Dakota): x-axis is pointing to the East.
68 ///c Muons are sampled on a surface of a rectangular parallelepiped,
69 ///c the planes of which are parallel to the walls of the cavern.
70 ///c The long side of the cavern is pointing at 6.6 deg from the North
71 ///c to the East (or 90 - 6.6 deg from the East to the North).
72 ///c Muon coordinates and direction cosines are then given in the
73 ///c coordinate system related to the cavern with x-axis
74 ///c pointing along the long side of the cavern at 6.6 deg from the
75 ///c North to the East.
76 ///c The angle phi is measured from the positive x-axis towards
77 ///c positive y-axis (0-360 deg).
78 ///c Z-axis is pointing upwards.
79 ///
80 /// Further notes taken from Vitaly's original code, as written for LBNE
81 /// (note the differing definition for theta, which is used here,
82 /// theta is measured from East to South, not North to East.
83 ///c
84 ///c The code samples single atmospheric muons at the LBNE
85 ///c underground site (taking into account the slant depth
86 ///c distribution calculated by Martin Richardson, Sheffield)
87 ///c in the energy range E1-E2, zenith angle range theta1-theta2 (0-90 degrees)
88 ///c and azimuthal angle range phi1-phi2 (0-360 degrees).
89 ///c At present only the following ranges of parameters are supported:
90 ///c E1 = 1 GeV, E2 = 10^6 GeV, theta1 = 0, theta2 = 90 deg, phi1 = 0, phi2 = 360 deg.
91 ///c
92 ///c The LBNE far detector site has coordinates:
93 ///c Latitude = 44° 20' 45.21" N
94 ///c Longitude = 103° 45' 16.13" W
95 ///c Elevation = 355.8 ft (108.4 m)
96 ///c from e-mail from Virgil Bocean on 30 May 2013
97 ///c confirmed by Tracy Lundin on 16 December 2013
98 ///c
99 ///c For slant depth distribution and muon intensities the azimuthal
100 ///c angle phi is calculated from East to North.
101 ///c The long side of the cavern is assumed to be pointing to the
102 ///c beam (Fermilab) at an angle of 7 deg to the South from the
103 ///c East.
104 ///c Muon direction cosines are given in the coordinate system
105 ///c related to the cavern with positive x-axis pointing to the beam
106 ///c so direction cosine wrt x-axis is -1 if a muon is coming in the
107 ///c same direction (wrt to x-axis) as the neutrino beam.
108 ///c
109 ///c
110 ///c Program uses muon energy spectra at various depths and zenith
111 ///c angles obtained with MUSIC code for muon propagation and Gaisser's
112 ///c formula for muon spectrum at sea level
113 ///c (T.K.Gaisser, Cosmic Rays and Particle Physics, Cambridge
114 ///c University Press, 1990) modified for large zenith angles and
115 ///c prompt muon flux with parameters from the best fit to the LVD data.
116 ///c
117 ///c Muon flux through a sphere = 6.65x10^(-9) cm^(-2) s^(-1) (gamma=2.77) - old
118 ///c Muon flux through a sphere = 6.65x10^(-9) cm^(-2) s^(-1) (gamma=2.77) - new
119 ///c Muon flux through the cuboid (100x40x50 m^3) = 0.3074 s^(-1) (gamma=2.77)
120 ///c
121 ///c Note: the muon spectrum at sea level does not take into account
122 ///c the change of the primary spectrum slope at and above the knee
123 ///c region (3*10^15-10^16 eV).
124 ///c
125 ///c Program uses the tables of muon energy spectra at various
126 ///c zenith and azimuthal angles at DUSEL
127 ///c calculated with the muon propagation code MUSIC and the
128 ///c angular distribution of muon intensities at 4850 ft level
129 ///c (location of the LBNE far detector).
130 ///c
131 ///c Coordinate system is determined by the mountain profile provided
132 ///c by Jeff de Jong (Cambridge) and Chao Zhang (USD, South Dakota).
133 ///c This is done to allow the simulation of muons on a surface of
134 ///c a rectangular parallelepiped, the planes of which are parallel
135 ///c to the walls of the laboratory halls.
136 ///c The angle phi in the slant depth distribution file is measured
137 ///c from the positive x-axis towards positive y-axis.
138 ///c Z-axis is pointing upwards.
139 ///c Note that I use here the azimuthal angle range from 0 to 360 deg.
140 ///c
141 ////////////////////////////////////////////////////////////////////////
142 
143 // C++ includes.
144 #include <cmath>
145 #include <cstdlib>
146 #include <exception>
147 #include <fstream>
148 #include <iostream>
149 #include <memory>
150 #include <sstream>
151 #include <string>
152 
153 // Framework includes
158 #include "art_root_io/TFileService.h"
159 #include "cetlib_except/exception.h"
160 #include "fhiclcpp/ParameterSet.h"
162 
163 // art extensions
164 #include "nurandom/RandomUtils/NuRandomService.h"
165 
166 // nusimdata includes
169 
170 // lar includes
173 
174 #include "TDatabasePDG.h"
175 #include "TTree.h"
176 
177 #include "CLHEP/Random/RandFlat.h"
178 #include "CLHEP/Random/RandGaussQ.h"
179 
180 // for c2: NTPCs is no longer used
181 //const int NTPCs = 300;
182 
183 namespace simb {
184  class MCTruth;
185 }
186 
187 namespace evgen {
188 
189  /// module to produce single or multiple specified particles in the detector
190  class MUSUN : public art::EDProducer {
191 
192  public:
193  explicit MUSUN(fhicl::ParameterSet const& pset);
194 
195  // This is called for each event.
196  void produce(art::Event& evt);
197  void beginJob();
198  void beginRun(art::Run& run);
199  void endRun(art::Run& run);
200 
201  private:
202  void SampleOne(unsigned int i, simb::MCTruth& mct, CLHEP::HepRandomEngine& engine);
203 
204  void initialization(double theta1,
205  double theta2,
206  double phi1,
207  double phi2,
208  int figflag,
209  double s_hor,
210  double s_ver1,
211  double s_ver2,
212  double& FI);
213 
214  void sampling(double& E,
215  double& theta,
216  double& phi,
217  double& dep,
218  CLHEP::HepRandomEngine& engine);
219 
220  static const int kGAUS = 1;
221 
222  CLHEP::HepRandomEngine& fEngine; ///< art-managed random-number engine
223 
224  int fPDG; ///< PDG code of particles to generate
225  double fChargeRatio; ///< Charge ratio of particle / anti-particle
226 
227  std::string fInputDir; ///< Input Directory
228  std::string fInputFile1; ///< Input File 1
229  std::string fInputFile2; ///< Input File 2
230  std::string fInputFile3; ///< Input File 3
231 
232  double fCavernAngle; ///< Angle of the detector from the North to the East.
233  double fRockDensity; ///< Default rock density is 2.70 g cm-3. If this is
234  ///< changed then the three input files need to be
235  ///< remade. If there is a desire for this contact
236  ///< Vitaly Kudryavtsev at V.Kudryavtsev@shef.ac.uk
237 
238  double fEmin; ///< Minimum Kinetic Energy (GeV)
239  double fEmax; ///< Maximum Kinetic Energy (GeV)
240 
241  double fThetamin; ///< Minimum theta
242  double fThetamax; ///< Maximum theta
243  double fPhimin; ///< Minimum phi
244  double fPhimax; ///< Maximum phi
245 
246  int figflag; ///< If want sampled from sphere or parallelepiped
247  double fXmin; ///< Minimum X position
248  double fYmin; ///< Minimum Y position
249  double fZmin; ///< Minimum Z position
250  double fXmax; ///< Maximum X position
251  double fYmax; ///< Maximum Y position
252  double fZmax; ///< Maximum Z position
253 
254  double fT0; ///< Central t position (ns) in world coordinates
255  double fSigmaT; ///< Variation in t position (ns)
256  int fTDist; ///< How to distribute t (gaus, or uniform)
257 
258  //Define TFS histograms.....
259  /*
260  TH1D* hPDGCode;
261  TH1D* hPositionX;
262  TH1D* hPositionY;
263  TH1D* hPositionZ;
264  TH1D* hTime;
265  TH1D* hMomentumHigh;
266  TH1D* hMomentum;
267  TH1D* hEnergyHigh;
268  TH1D* hEnergy;
269  TH1D* hDepth;
270  TH1D* hDirCosineX;
271  TH1D* hDirCosineY;
272  TH1D* hDirCosineZ;
273  TH1D* hTheta;
274  TH1D* hPhi;
275  */
276  int PdgCode;
277  double Energy, phi, theta, dep, Time;
278  double Momentum, px0, py0, pz0;
279  double x0, y0, z0, cx, cy, cz;
280 
281  //Define some variables....
282  double FI = 0.;
283  double s_hor = 0.;
284  double s_ver1 = 0.;
285  double s_ver2 = 0.;
286 
287  double spmu[121][62][51];
288  double fnmu[32401];
289  double depth[360][91];
290  double fmu[360][91];
291  // for c2: e1 and e2 are unused
292  //double e1, e2, the1, the2, ph1, ph2;
293  double the1, the2, ph1, ph2;
294  double se = 0.;
295  double st = 0.;
296  double sp = 0.;
297  double sd = 0.;
298 
299  unsigned int NEvents = 0;
300 
301  // TTree
302  TTree* fTree;
303  /*
304  // TTree for CryoPos
305  TTree* fCryos;
306  int NumTPCs;
307  double TPCMinX[NTPCs];
308  double TPCMaxX[NTPCs];
309  double TPCMinY[NTPCs];
310  double TPCMaxY[NTPCs];
311  double TPCMinZ[NTPCs];
312  double TPCMaxZ[NTPCs];
313  double CryoSize[6];
314  double DetHall[6];
315  */
316  };
317 }
318 
319 namespace evgen {
320 
321  //____________________________________________________________________________
322  MUSUN::MUSUN(fhicl::ParameterSet const& pset)
323  : art::EDProducer{pset}
324  // create a default random engine; obtain the random seed from NuRandomService,
325  // unless overridden in configuration with key "Seed"
326  , fEngine(art::ServiceHandle<rndm::NuRandomService> {}->createEngine(*this, pset, "Seed"))
327  , fPDG{pset.get<int>("PDG")}
328  , fChargeRatio{pset.get<double>("ChargeRatio")}
329  , fInputDir{pset.get<std::string>("InputDir")}
330  , fInputFile1{pset.get<std::string>("InputFile1")}
331  , fInputFile2{pset.get<std::string>("InputFile2")}
332  , fInputFile3{pset.get<std::string>("InputFile3")}
333  , fCavernAngle{pset.get<double>("CavernAngle")}
334  , fRockDensity{pset.get<double>("RockDensity")}
335  , fEmin{pset.get<double>("Emin")}
336  , fEmax{pset.get<double>("Emax")}
337  , fThetamin{pset.get<double>("Thetamin")}
338  , fThetamax{pset.get<double>("Thetamax")}
339  , fPhimin{pset.get<double>("Phimin")}
340  , fPhimax{pset.get<double>("Phimax")}
341  , figflag{pset.get<int>("igflag")}
342  , fXmin{pset.get<double>("Xmin")}
343  , fYmin{pset.get<double>("Ymin")}
344  , fZmin{pset.get<double>("Zmin")}
345  , fXmax{pset.get<double>("Xmax")}
346  , fYmax{pset.get<double>("Ymax")}
347  , fZmax{pset.get<double>("Zmax")}
348  , fT0{pset.get<double>("T0")}
349  , fSigmaT{pset.get<double>("SigmaT")}
350  , fTDist{pset.get<int>("TDist")}
351  {
352  produces<std::vector<simb::MCTruth>>();
353  produces<sumdata::RunData, art::InRun>();
354  }
355 
356  ////////////////////////////////////////////////////////////////////////////////
357  // Begin Job
358  ////////////////////////////////////////////////////////////////////////////////
359  void
361  {
362  // Make the Histograms....
364  /*
365  hPDGCode = tfs->make<TH1D>("hPDGCode" ,"PDG Code" ,30 , -15 , 15 );
366  hPositionX = tfs->make<TH1D>("hPositionX" ,"Position (cm)" ,500, ( fXmin - 10 ), ( fXmax + 10 ) );
367  hPositionY = tfs->make<TH1D>("hPositionY" ,"Position (cm)" ,500, ( fYmin - 10 ), ( fYmax + 10 ) );
368  hPositionZ = tfs->make<TH1D>("hPositionZ" ,"Position (cm)" ,500, ( fZmin - 10 ), ( fZmax + 10 ) );
369  hTime = tfs->make<TH1D>("hTime" ,"Time (s)" ,500, 0 , 1e6 );
370  hMomentumHigh = tfs->make<TH1D>("hMomentumHigh","Momentum (GeV)",500, fEmin, fEmax );
371  hMomentum = tfs->make<TH1D>("hMomentum" ,"Momentum (GeV)",500, fEmin, fEmin+1e3 );
372  hEnergyHigh = tfs->make<TH1D>("hEnergyHigh" ,"Energy (GeV)" ,500, fEmin, fEmax );
373  hEnergy = tfs->make<TH1D>("hEnergy" ,"Energy (GeV)" ,500, fEmin, fEmin+1e3 );
374  hDepth = tfs->make<TH1D>("hDepth" ,"Depth (m)" ,800, 0 , 14000 );
375 
376  hDirCosineX = tfs->make<TH1D>("hDirCosineX","Normalised Direction cosine",500, -1, 1 );
377  hDirCosineY = tfs->make<TH1D>("hDirCosineY","Normalised Direction cosine",500, -1, 1 );
378  hDirCosineZ = tfs->make<TH1D>("hDirCosineZ","Normalised Direction cosine",500, -1, 1 );
379 
380  hTheta = tfs->make<TH1D>("hTheta" ,"Angle (degrees)",500, 0, 90 );
381  hPhi = tfs->make<TH1D>("hPhi" ,"Angle (degrees)",500, 0, 365 );
382  */
383  fTree = tfs->make<TTree>("Generator", "analysis tree");
384  fTree->Branch("particleID", &PdgCode, "particleID/I");
385  fTree->Branch("energy", &Energy, "energy/D");
386  fTree->Branch("time", &Time, "Time/D");
387  fTree->Branch("posX", &x0, "posX/D");
388  fTree->Branch("posY", &y0, "posY/D");
389  fTree->Branch("posZ", &z0, "posZ/D");
390  fTree->Branch("cosX", &cx, "cosX/D");
391  fTree->Branch("cosY", &cy, "cosY/D");
392  fTree->Branch("cosZ", &cz, "cosZ/D");
393  fTree->Branch("theta", &theta, "theta/D");
394  fTree->Branch("phi", &phi, "phi/D");
395  fTree->Branch("depth", &dep, "dep/D");
396  /*
397  fCryos = tfs->make<TTree>("CryoSizes","cryo tree");
398  fCryos->Branch("NumTPCs" , &NumTPCs , "NumTPCs/I" );
399  fCryos->Branch("TPCMinX" , &TPCMinX , "TPCMinX[NumTPCs]/D");
400  fCryos->Branch("TPCMaxX" , &TPCMaxX , "TPCMaxX[NumTPCs]/D");
401  fCryos->Branch("TPCMinY" , &TPCMinY , "TPCMinY[NumTPCs]/D");
402  fCryos->Branch("TPCMaxY" , &TPCMaxY , "TPCMaxY[NumTPCs]/D");
403  fCryos->Branch("TPCMinZ" , &TPCMinZ , "TPCMinZ[NumTPCs]/D");
404  fCryos->Branch("TPCMaxZ" , &TPCMaxZ , "TPCMaxZ[NumTPCs]/D");
405  fCryos->Branch("CryoSize", &CryoSize, "CryoSize[6]/D" );
406  fCryos->Branch("DetHall" , &DetHall , "DetHall[6]/D" );
407  */
408  }
409 
410  ////////////////////////////////////////////////////////////////////////////////
411  // Begin Run
412  ////////////////////////////////////////////////////////////////////////////////
413  void
415  {
416  // Check fcl parameters were set correctly
417  if (fThetamax > 90.5)
418  throw cet::exception("MUSUNGen")
419  << "\nThetamax has to be less than " << M_PI / 2 << ", but was entered as " << fThetamax
420  << ", this causes an error so leaving program now...\n\n";
421  if (fThetamin < 0)
422  throw cet::exception("MUSUNGen")
423  << "\nThetamin has to be more than 0, but was entered as " << fThetamin
424  << ", this causes an error so leaving program now...\n\n";
425  if (fThetamax < fThetamin)
426  throw cet::exception("MUSUNGen") << "\nMinimum angle is bigger than maximum angle....causes "
427  "an error so leaving program now....\n\n";
428  if (fPhimax > 360.5)
429  throw cet::exception("MUSUNGen")
430  << "\nPhimax has to be less than " << 2 * M_PI << ", but was entered as " << fPhimax
431  << ", this cause an error so leaving program now...\n\n";
432  if (fPhimin < 0)
433  throw cet::exception("MUSUNGen")
434  << "\nPhimin has to be more than 0, but was entered as " << fPhimin
435  << ", this causes an error so leaving program now...\n\n";
436  if (fPhimax < fPhimin)
437  throw cet::exception("MUSUNGen") << "\nMinimum angle is bigger than maximum angle....causes "
438  "an error so leaving program now....\n\n";
439  if (fEmax < fEmin)
440  throw cet::exception("MUSUNGen")
441  << "\nMinimum energy is bigger than maximum energy....causes an error so leaving program "
442  "now....\n\n";
443 
444  // grab the geometry object to see what geometry we are using
446  run.put(std::make_unique<sumdata::RunData>(geo->DetectorName()));
447 
448  // area of the horizontal plane of the parallelepiped
449  s_hor = (fZmax - fZmin) * (fXmax - fXmin);
450  // area of the vertical plane of the parallelepiped, perpendicular to z-axis
451  s_ver1 = (fXmax - fXmin) * (fYmax - fYmin);
452  // area of the vertical plane of the parallelepiped, perpendicular to x-axis
453  s_ver2 = (fZmax - fZmin) * (fYmax - fYmin);
454 
455  //std::cout << s_hor << " " << s_ver1 << " " << s_ver2 << std::endl;
456 
458 
459  std::cout << "Material - SURF rock" << std::endl;
460  std::cout << "Density = " << fRockDensity << " g/cm^3" << std::endl;
461  std::cout << "Parameters for muon spectrum are from LVD best fit" << std::endl;
462  std::cout << "Muon energy range = " << fEmin << " - " << fEmax << " GeV" << std::endl;
463  std::cout << "Zenith angle range = " << fThetamin << " - " << fThetamax << " degrees"
464  << std::endl;
465  std::cout << "Azimuthal angle range = " << fPhimin << " - " << fPhimax << " degrees"
466  << std::endl;
467  std::cout << "Global intensity = " << FI
468  << " (cm^2 s)^(-1) or s^(-1) (for muons on the surface)" << std::endl;
469  /*
470  NumTPCs = geo->NTPC(0);
471  std::cout << "There are " << NumTPCs << " in cryostat 0" << std::endl;
472  for (unsigned int c=0; c<geo->Ncryostats(); c++) {
473  const geo::CryostatGeo& cryostat=geo->Cryostat(c);
474  geo->CryostatBoundaries( CryoSize, 0 );
475  std::cout << "Cryo bounds " << CryoSize[0] << " "<< CryoSize[1] << " "<< CryoSize[2] << " "<< CryoSize[3] << " "<< CryoSize[4] << " "<< CryoSize[5] << std::endl;
476  for (unsigned int t=0; t<cryostat.NTPC(); t++) {
477  geo::TPCID id;
478  id.Cryostat=c;
479  id.TPC=t;
480  id.isValid=true;
481  const geo::TPCGeo& tpc=cryostat.TPC(id);
482  TPCMinX[t] = tpc.MinX();
483  TPCMaxX[t] = tpc.MaxX();
484  TPCMinY[t] = tpc.MinY();
485  TPCMaxY[t] = tpc.MaxY();
486  TPCMinZ[t] = tpc.MinZ();
487  TPCMaxZ[t] = tpc.MaxZ();
488  std::cout << t << "\t" << TPCMinX[t] << " " << TPCMaxX[t] << " " << TPCMinY[t] << " " << TPCMaxY[t] << " " << TPCMinZ[t] << " " << TPCMaxZ[t] << std::endl;
489  }
490  }
491  fCryos -> Fill();
492  */
493  return;
494  }
495 
496  ////////////////////////////////////////////////////////////////////////////////
497  // End Run
498  ////////////////////////////////////////////////////////////////////////////////
499  void
501  {
502  std::cout << "\n\nNumber of muons = " << NEvents << std::endl;
503  std::cout << "Mean muon energy = " << se / NEvents << " GeV" << std::endl;
504  std::cout << "Mean zenith angle (theta) = " << st / NEvents << " degrees" << std::endl;
505  std::cout << "Mean azimuthal angle (phi)= " << sp / NEvents << " degrees" << std::endl;
506  std::cout << "Mean slant depth = " << sd / NEvents << " m w.e." << std::endl;
507  }
508  ////////////////////////////////////////////////////////////////////////////////
509  // Produce
510  ////////////////////////////////////////////////////////////////////////////////
511  void
513  {
514  ///unique_ptr allows ownership to be transferred to the art::Event after the put statement
515  std::unique_ptr<std::vector<simb::MCTruth>> truthcol(new std::vector<simb::MCTruth>);
516 
517  simb::MCTruth truth;
519 
520  ++NEvents;
521 
522  SampleOne(NEvents, truth, fEngine);
523 
524  MF_LOG_DEBUG("MUSUN") << truth;
525 
526  truthcol->push_back(truth);
527 
528  evt.put(std::move(truthcol));
529  }
530  ////////////////////////////////////////////////////////////////////////////////
531  // Sample One
532  ////////////////////////////////////////////////////////////////////////////////
533  // Draw the type, momentum and position of a single particle from the
534  // FCIHL description
535  void
536  MUSUN::SampleOne(unsigned int i, simb::MCTruth& mct, CLHEP::HepRandomEngine& engine)
537  {
538  CLHEP::RandFlat flat(engine);
539  CLHEP::RandGaussQ gauss(engine);
540 
541  Energy = 0;
542  theta = 0;
543  phi = 0;
544  dep = 0;
545  Time = 0;
546 
547  sampling(Energy, theta, phi, dep, engine);
548 
549  theta = theta * M_PI / 180;
550 
551  // changing the angle phi so z-axis is positioned along the long side
552  // of the cavern pointing at 14 deg from the North to the East.
553  // phi += (90. - 14.0);
554  // Want our co-ord rotation going from East to South.
555  phi += fCavernAngle;
556  if (phi >= 360.) phi -= 360.;
557  if (phi < 0) phi += 360.;
558  phi *= M_PI / 180.;
559 
560  // set track id to -i as these are all primary particles and have id <= 0
561  int trackid = -1 * (i + 1);
562  std::string primary("primary");
563 
564  // Work out whether particle/antiparticle, and mass...
565  double m = 0.0;
566  PdgCode = fPDG;
567  double ChargeCheck = 1. / (1 + fChargeRatio);
568  double pdgfire = flat.fire();
569  if (pdgfire < ChargeCheck) PdgCode = -PdgCode;
570 
571  static TDatabasePDG pdgt;
572  TParticlePDG* pdgp = pdgt.GetParticle(PdgCode);
573  if (pdgp) m = pdgp->Mass();
574 
575  //std::cout << pdgfire << " " << ChargeCheck << " " << PdgCode << " " << m << std::endl;
576 
577  // Work out T0...
578  if (fTDist == kGAUS) { Time = gauss.fire(fT0, fSigmaT); }
579  else {
580  Time = fT0 + fSigmaT * (2.0 * flat.fire() - 1.0);
581  }
582 
583  // The minus sign above is for y-axis pointing up, so the y-momentum
584  // is always pointing down
585  cx = +sin(theta) * sin(phi);
586  cy = -cos(theta);
587  cz = +sin(theta) * cos(phi);
588  Momentum = std::sqrt(Energy * Energy - m * m); // Get momentum
589  px0 = Momentum * cx;
590  py0 = Momentum * cy;
591  pz0 = Momentum * cz;
592  TLorentzVector pvec(px0, py0, pz0, Energy);
593 
594  // Muon coordinates
595  double sh1 = s_hor * cos(theta);
596  double sv1 = s_ver1 * sin(theta) * fabs(cos(phi));
597  double sv2 = s_ver2 * sin(theta) * fabs(sin(phi));
598  double ss = sh1 + sv1 + sv2;
599  double xfl1 = flat.fire();
600  if (xfl1 <= sh1 / ss) {
601  x0 = (fXmax - fXmin) * flat.fire() + fXmin;
602  y0 = fYmax;
603  z0 = (fZmax - fZmin) * flat.fire() + fZmin;
604  }
605  else if (xfl1 <= (sh1 + sv1) / ss) {
606  x0 = (fXmax - fXmin) * flat.fire() + fXmin;
607  y0 = (fYmax - fYmin) * flat.fire() + fYmin;
608  if (cz >= 0)
609  z0 = fZmin;
610  else
611  z0 = fZmax;
612  }
613  else {
614  if (cx >= 0)
615  x0 = fXmin;
616  else
617  x0 = fXmax;
618  y0 = (fYmax - fYmin) * flat.fire() + fYmin;
619  z0 = (fZmax - fZmin) * flat.fire() + fZmin;
620  }
621  // Make Lorentz vector for x and t....
622  TLorentzVector pos(x0, y0, z0, Time);
623 
624  // Parameters written to the file muons_surf_v2_test*.dat
625  // nmu - muon sequential number
626  // id_part - muon charge (10 - positive, 11 - negative )
627  // Energy - total muon energy in GeV assuming ultrarelativistic muons
628  // x0, y0, z0 - muon coordinates on the surface of parallelepiped
629  // specified above; x-axis and y-axis are pointing in the
630  // directions such that the angle phi (from the slant depth
631  // distribution files) is measured from x to y. z-axis is
632  // pointing upwards.
633  // cx, cy, cz - direction cosines.
634 
635  simb::MCParticle part(trackid, PdgCode, primary);
636  part.AddTrajectoryPoint(pos, pvec);
637 
638  mct.Add(part);
639 
640  theta = theta * 180 / M_PI;
641  phi = phi * 180 / M_PI;
642 
643  // Sum energies, angles, depth for average outputs.
644  se += Energy;
645  st += theta;
646  sp += phi;
647  sd += dep;
648 
649  // Fill Histograms.....
650  /*
651  hPDGCode ->Fill (PdgCode);
652  hPositionX ->Fill (x0);
653  hPositionY ->Fill (y0);
654  hPositionZ ->Fill (z0);
655  hTime ->Fill (Time);
656  hMomentumHigh ->Fill (Momentum);
657  hMomentum ->Fill (Momentum);
658  hEnergyHigh ->Fill (Energy);
659  hEnergy ->Fill (Energy);
660  hDepth ->Fill (dep);
661  hDirCosineX ->Fill (cx);
662  hDirCosineY ->Fill (cy);
663  hDirCosineZ ->Fill (cz);
664  hTheta ->Fill (theta);
665  hPhi ->Fill (phi);
666  */
667  /*
668  // Write event by event outsputs.....
669  std::cout << "Theta: " << theta << " Phi: " << phi << " Energy: " << Energy << " Momentum: " << Momentum << std::endl;
670  std::cout << "x: " << pos.X() << " y: " << pos.Y() << " z: " << pos.Z() << " time: " << pos.T() << std::endl;
671  std::cout << "Px: " << pvec.Px() << " Py: " << pvec.Py() << " Pz: " << pvec.Pz() << std::endl;
672  std::cout << "Normalised..." << cx << " " << cy << " " << cz << std::endl;
673  */
674  fTree->Fill();
675  }
676 
677  ////////////////////////////////////////////////////////////////////////////////
678  // initialization
679  ////////////////////////////////////////////////////////////////////////////////
680  void
681  MUSUN::initialization(double theta1,
682  double theta2,
683  double phi1,
684  double phi2,
685  int figflag,
686  double s_hor,
687  double s_ver1,
688  double s_ver2,
689  double& FI)
690  {
691  //
692  // Read in the data files
693  //
694  int lineNumber = 0, index = 0;
695  char inputLine[10000];
696  std::string fROOTfile;
697 
698  for (int a = 0; a < 121; ++a)
699  for (int b = 0; b < 62; ++b)
700  for (int c = 0; c < 50; ++c)
701  spmu[a][b][c] = 0;
702  for (int a = 0; a < 23401; ++a)
703  fnmu[a] = 0;
704  for (int a = 0; a < 360; ++a)
705  for (int b = 0; b < 91; ++b)
706  depth[a][b] = 0;
707  for (int a = 0; a < 360; ++a)
708  for (int b = 0; b < 91; ++b)
709  fmu[a][b] = 0;
710 
711  std::ostringstream File1LocStream;
712  File1LocStream << fInputDir << fInputFile1;
713  std::string File1Loc = File1LocStream.str();
714  cet::search_path sp1("FW_SEARCH_PATH");
715  if (sp1.find_file(fInputFile1, fROOTfile)) File1Loc = fROOTfile;
716  std::ifstream file1(File1Loc.c_str(), std::ios::in);
717  if (!file1.good())
718  throw cet::exception("MUSUNGen")
719  << "\nFile1 " << fInputFile1 << " not found in FW_SEARCH_PATH or at " << fInputDir
720  << "\n\n";
721 
722  while (file1.good()) {
723  //std::cout << "Looking at file 1...." << std::endl;
724  file1.getline(inputLine, 9999);
725  char* token;
726  token = strtok(inputLine, " ");
727  while (token != NULL) {
728  //std::cout << "While loop file 1..." << std::endl;
729  fmu[index][lineNumber] = atof(token);
730  token = strtok(NULL, " ");
731  index++;
732  if (index == 360) {
733  //std::cout << "If statement file 1..." << std::endl;
734  index = 0;
735  lineNumber++;
736  }
737  }
738  }
739  file1.close();
740 
741  std::ostringstream File2LocStream;
742  File2LocStream << fInputDir << fInputFile2;
743  std::string File2Loc = File2LocStream.str();
744  cet::search_path sp2("FW_SEARCH_PATH");
745  if (sp2.find_file(fInputFile2, fROOTfile)) File2Loc = fROOTfile;
746  std::ifstream file2(File2Loc.c_str(), std::ios::binary | std::ios::in);
747  if (!file2.good())
748  throw cet::exception("MUSUNGen")
749  << "\nFile2 " << fInputFile2 << " not found in FW_SEARCH_PATH or at " << fInputDir
750  << "\n\n";
751 
752  int i1 = 0, i2 = 0, i3 = 0;
753  float readVal;
754  while (file2.good()) {
755  //std::cout << "Looking at file 2...." << std::endl;
756  file2.read((char*)(&readVal), sizeof(float));
757  spmu[i1][i2][i3] = readVal;
758  i1++;
759  if (i1 == 121) {
760  //std::cout << "First if statement file 2..." << std::endl;
761  i2++;
762  i1 = 0;
763  }
764  if (i2 == 62) {
765  //std::cout << "Second if statement file 2..." << std::endl;
766  i3++;
767  i2 = 0;
768  }
769  }
770  file2.close();
771  for (int i = 0; i < 120; i++)
772  for (int j = 0; j < 62; j++)
773  for (int k = 0; k < 51; k++)
774  spmu[i][j][k] = spmu[i + 1][j][k];
775  spmu[1][1][0] = 0.000853544;
776  //std::cout << "Set spmu to some value..." << std::endl;
777 
778  std::ostringstream File3LocStream;
779  File3LocStream << fInputDir << fInputFile3;
780  std::string File3Loc = File3LocStream.str();
781  cet::search_path sp3("FW_SEARCH_PATH");
782  if (sp3.find_file(fInputFile3, fROOTfile)) File3Loc = fROOTfile;
783  std::ifstream file3(File3Loc.c_str(), std::ios::in);
784  if (!file3.good())
785  throw cet::exception("MUSUNGen")
786  << "\nFile3 " << fInputFile3 << " not found in FW_SEARCH_PATH or at " << fInputDir
787  << "\n\n";
788 
789  lineNumber = index = 0;
790  while (file3.good()) {
791  //std::cout << "Looking at file 3...." << std::endl;
792  file3.getline(inputLine, 9999);
793  char* token;
794  token = strtok(inputLine, " ");
795  while (token != NULL) {
796  //std::cout << "While loop file 3..." << std::endl;
797  depth[index][lineNumber] = atof(token);
798  token = strtok(NULL, " ");
799  index++;
800  if (index == 360) {
801  //std::cout << "If statement file 3..." << std::endl;
802  index = 0;
803  lineNumber++;
804  }
805  }
806  }
807  file3.close();
808 
809  //
810  // Set up variables
811  //
812 
813  the1 = theta1;
814  the2 = theta2;
815  // for c2: c1 and c2 are unused
816  //double c1 = cos(M_PI/180.*theta1);
817  //double c2 = cos(M_PI/180.*theta2);
818  ph1 = M_PI / 180. * phi1;
819  ph2 = M_PI / 180. * phi2;
820  // for c2: dph is unused
821  //double dph = ph2-ph1;
822 
823  int ipc = 1;
824  double theta = theta1;
825  double dc = 1.;
826  double sc = 0.;
827  int iteration = 0;
828  while (theta < theta2 - dc / 2.) {
829  theta += dc / 2.;
830  double theta0 = M_PI / 180. * theta;
831  double cc = cos(theta0);
832  double ash = s_hor * cc;
833  double asv01 = s_ver1 * sqrt(1. - cc * cc);
834  double asv02 = s_ver2 * sqrt(1. - cc * cc);
835  int ic1 = (theta + 0.999);
836  int ic2 = ic1 + 1;
837  if (ic2 > 91) ic2 = 91;
838  if (ic1 < 1) ic1 = 1;
839  double phi = phi1;
840  double dp = 1.;
841 
842  while (phi < phi2 - dp / 2.) {
843  phi += dp / 2.;
844  // the long side of the cavern is pointing at 14 deg to the north:
845  // double phi0 = M_PI / 180. * (phi + 90. - 14);
846 
847  // Want our co-ord system going from East to South.
848  double phi0 = M_PI / 180. * (phi + fCavernAngle);
849 
850  double asv1 = asv01 * fabs(cos(phi0));
851  double asv2 = asv02 * fabs(sin(phi0));
852  double asv0 = ash + asv1 + asv2;
853  double fl = 1.;
854  if (figflag == 1) fl = asv0;
855  int ip1 = (phi + 0.999);
856  int ip2 = ip1 + 1;
857  if (ip2 > 360) ip2 = 1;
858  if (ip1 < 1) ip1 = 360;
859  double sp1 = 0.;
860 
861  for (int ii = 0; ii < 4; ii++) {
862  int iic = ii / 2;
863  int iip = ii % 2;
864  if (ip1 == 360 && (ii == 1 || ii == 3)) iip = -359;
865  if (fmu[ip1 + iip - 1][ic1 + iic - 1] < 0) {
866  if (pow(10., fmu[ip1 + iip - 1][ic1 + iic - 1]) / 4 > 1e-6) {
867  std::cout << "Looking at fmu [ " << ip1 << " + " << iip << " - 1 (" << ip1 + iip - 1
868  << ") ] [ " << ic1 << " + " << iic << " - 1 (" << ic1 + iic - 1 << ") ] ."
869  << "\nChanging sp1 from " << sp1 << " to "
870  << sp1 + pow(10., fmu[ip1 + iip - 1][ic1 + iic - 1]) / 4 << "..........."
871  << sp1 << " + 10 ^ (" << fmu[ip1 + iip - 1][ic1 + iic - 1] << ") / 4 "
872  << std::endl;
873  }
874  sp1 = sp1 + pow(10., fmu[ip1 + iip - 1][ic1 + iic - 1]) / 4;
875  }
876  }
877  /*
878  std::cout << iteration<< " time of new sc value! Theta " << theta << ", phi " << phi + dp / 2. << ", sc = " << sc + sp1 * fl * dp * M_PI / 180. * sin(theta0) * dc * M_PI / 180. << " = "
879  << sc << " + " << sp1 << " * " << fl << " * " << dp << " * " << M_PI/180 << " * sin(" << theta0 << ") * " << dc << " * " << M_PI/180 << ".....sin(theta)=" << sin(theta) << "\n"
880  << std::endl; */
881  sc = sc + sp1 * fl * dp * M_PI / 180. * sin(theta0) * dc * M_PI / 180.;
882  ++iteration;
883  ipc = ipc + 1;
884  fnmu[ipc - 1] = sc;
885  phi = phi + dp / 2.;
886  }
887 
888  theta = theta + dc / 2.;
889  }
890  //std::cout << *FI << " = " << sc << std::endl;
891  FI = sc;
892  for (int ipc1 = 0; ipc1 < ipc; ipc1++)
893  fnmu[ipc1] = fnmu[ipc1] / fnmu[ipc - 1];
894  }
895 
896  ////////////////////////////////////////////////////////////////////////////////
897  // sampling
898  ////////////////////////////////////////////////////////////////////////////////
899  void
901  double& theta,
902  double& phi,
903  double& dep,
904  CLHEP::HepRandomEngine& engine)
905  {
906  CLHEP::RandFlat flat(engine);
907  CLHEP::RandGaussQ gauss(engine);
908 
909 #if 0 // this code is disabled for good
910  double xfl = flat.fire();
911  int loIndex = 0, hiIndex = 32400;
912  int i = (loIndex+hiIndex)/2;
913  bool foundIndex = false;
914  if( xfl < fnmu[loIndex] ) {
915  i = loIndex;
916  foundIndex = true;
917  } else if ( xfl > fnmu[hiIndex] ) {
918  i = hiIndex;
919  foundIndex = true;
920  } else if ( xfl > fnmu[i-1] && xfl <= fnmu[i] )
921  foundIndex = true;
922  while( !foundIndex ) {
923  if( xfl < fnmu[i] )
924  hiIndex = i;
925  else
926  loIndex = i;
927  i = (loIndex + hiIndex)/2;
928 
929  if( xfl > fnmu[i-1] && xfl <= fnmu[i] )
930  foundIndex = true;
931  }
932 #else
933  double xfl = flat.fire();
934  int i = 0;
935  while (xfl > fnmu[i])
936  ++i;
937 #endif
938  int ic = (i - 2) / 360;
939  int ip = i - 2 - ic * 360;
940 
941  xfl = flat.fire();
942  theta = the1 + ((double)ic + xfl);
943  xfl = flat.fire();
944  phi = ph1 + ((double)ip + xfl);
945  if (phi > 360) phi = phi - 360;
946  dep = depth[ip][ic] * fRockDensity;
947 
948  int ic1 = cos(M_PI / 180. * theta) * 50.;
949  if (ic1 < 0) ic1 = 0;
950  if (ic1 > 50) ic1 = 50;
951  int ip1 = dep / 200. - 16;
952  if (ip1 < 0) ip1 = 0;
953  if (ip1 > 61) ip1 = 61;
954 
955  xfl = flat.fire();
956 #if 0
957  loIndex = 0, hiIndex = 120;
958  int j = (loIndex+hiIndex)/2;
959  foundIndex = false;
960  if( xfl < spmu[loIndex][ip1][ic1] ) {
961  j = loIndex;
962  foundIndex = true;
963  } else if ( xfl > spmu[hiIndex][ip1][ic1] ) {
964  j = hiIndex;
965  foundIndex = true;
966  } else if ( xfl > spmu[j-1][ip1][ic1] && xfl <= spmu[j][ip1][ic1] )
967  foundIndex = true;
968  while( !foundIndex ) {
969  if( xfl < spmu[j][ip1][ic1] )
970  hiIndex = j;
971  else
972  loIndex = j;
973  j = (loIndex + hiIndex)/2;
974 
975  if( xfl > spmu[j-1][ip1][ic1] && xfl <= spmu[j][ip1][ic1] )
976  foundIndex = true;
977  }
978 #else
979  int j = 0;
980  while (xfl > spmu[j][ip1][ic1])
981  ++j;
982 #endif
983 
984  double En1 = 0.05 * (j - 1);
985  double En2 = 0.05 * (j);
986  E = pow(10., En1 + (En2 - En1) * flat.fire());
987 
988  return;
989  }
990 
991 } //end namespace evgen
992 
993 namespace evgen {
994 
996 
997 } //end namespace evgen
unsigned int NEvents
double fEmax
Maximum Kinetic Energy (GeV)
void beginRun(art::Run &run)
double fZmax
Maximum Z position.
double fYmax
Maximum Y position.
double fCavernAngle
Angle of the detector from the North to the East.
void AddTrajectoryPoint(TLorentzVector const &position, TLorentzVector const &momentum)
Definition: MCParticle.h:257
void SetOrigin(simb::Origin_t origin)
Definition: MCTruth.h:82
double fEmin
Minimum Kinetic Energy (GeV)
std::string string
Definition: nybbler.cc:12
double fThetamin
Minimum theta.
std::string fInputDir
Input Directory.
constexpr T pow(T x)
Definition: pow.h:72
Particle class.
static const int kGAUS
double fT0
Central t position (ns) in world coordinates.
art framework interface to geometry description
Definition: Run.h:17
int fTDist
How to distribute t (gaus, or uniform)
double fRockDensity
int fPDG
PDG code of particles to generate.
const double e
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:67
void endRun(art::Run &run)
void beginJob()
Definition: Breakpoints.cc:14
std::string DetectorName() const
Returns a string with the name of the detector, as configured.
double fmu[360][91]
double fPhimax
Maximum phi.
single particles thrown at the detector
Definition: MCTruth.h:26
const double a
def move(depos, offset)
Definition: depos.py:107
std::string fInputFile3
Input File 3.
double fPhimin
Minimum phi.
double fChargeRatio
Charge ratio of particle / anti-particle.
std::string fInputFile2
Input File 2.
ProductID put(std::unique_ptr< PROD > &&edp, std::string const &instance={})
Definition: DataViewImpl.h:686
double depth[360][91]
Base utilities and modules for event generation and detector simulation.
int figflag
If want sampled from sphere or parallelepiped.
double spmu[121][62][51]
module to produce single or multiple specified particles in the detector
#define M_PI
Definition: includeROOT.h:54
void initialization(double theta1, double theta2, double phi1, double phi2, int figflag, double s_hor, double s_ver1, double s_ver2, double &FI)
void SampleOne(unsigned int i, simb::MCTruth &mct, CLHEP::HepRandomEngine &engine)
double fYmin
Minimum Y position.
void Add(simb::MCParticle const &part)
Definition: MCTruth.h:80
double fZmin
Minimum Z position.
double fSigmaT
Variation in t position (ns)
double fnmu[32401]
#define MF_LOG_DEBUG(id)
std::string find_file(std::string const &filename) const
Definition: search_path.cc:96
static bool * b
Definition: config.cpp:1043
CLHEP::HepRandomEngine & fEngine
art-managed random-number engine
double fThetamax
Maximum theta.
double fXmin
Minimum X position.
void produce(art::Event &evt)
TCEvent evt
Definition: DataStructs.cxx:7
Event generator information.
Definition: MCTruth.h:32
LArSoft geometry interface.
Definition: ChannelGeo.h:16
void sampling(double &E, double &theta, double &phi, double &dep, CLHEP::HepRandomEngine &engine)
Event Generation using GENIE, cosmics or single particles.
std::string fInputFile1
Input File 1.
Definition: se.py:1
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
double fXmax
Maximum X position.
QTextStream & endl(QTextStream &s)