GENIEGenerator
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
gBeamHNLValidationApp.cxx
Go to the documentation of this file.
1 //________________________________________________________________________________________
2 /*!
3 
4 \program gevald_hnl
5 
6 \brief Validation app for HNL generation.
7 
8  *** Synopsis :
9 
10  gevald_hnl [-h]
11  [-r run#]
12  -n n_of_events
13  -f path/to/flux/files
14  [-E hnl_energy]
15  [-g geometry (ROOT file)]
16  [-L geometry_length_units]
17  [-A geometry_angle_units]
18  [-T geometry_time_units]
19  [-o output_event_file_prefix]
20  [--seed random_number_seed]
21 
22  *** Options :
23 
24  [] Denotes an optional argument
25 
26  -h
27  Prints out the gevald_hnl syntax and exits.
28  -r
29  Specifies the MC run number [default: 1000].
30  -n
31  Specifies how many events to generate.
32  -f
33  Input HNL flux.
34  -E
35  HNL energy for monoenergetic HNL
36  -g
37  Input detector geometry.
38  If a geometry is specified, HNL decay vertices will be distributed
39  in the desired detector volume.
40  Using this argument, you can pass a ROOT file containing your
41  detector geometry description.
42  -L
43  Input geometry length units, eg 'm', 'cm', 'mm', ...
44  [default: 'mm']
45  -A
46  Input geometry angle units: 'deg', 'rad', or 'mrad'
47  [default: 'rad']
48  -T
49  Input geometry time units, eg 's', 'us', 'ns', ...
50  [default: 'ns']
51  -o
52  Sets the prefix of the output event file.
53  The output filename is built as:
54  [prefix].[run_number].[event_tree_format].[file_format]
55  The default output filename is:
56  gntp.[run_number].ghep.root
57  This cmd line arguments lets you override 'gntp'
58  --seed
59  Random number seed.
60 
61 \author Costas Andreopoulos <c.andreopoulos \at cern.ch>
62  University of Liverpool
63 
64  John Plows <komninos-john.plows \at cern.ch>
65  University of Oxford
66 
67 \created May 17, 2022
68 
69 \cpright Copyright (c) 2003-2024, The GENIE Collaboration
70  For the full text of the license visit http://copyright.genie-mc.org
71 
72 */
73 //_________________________________________________________________________________________
74 
75 #include <cassert>
76 #include <cstdlib>
77 #include <string>
78 #include <vector>
79 #include <sstream>
80 
81 #include <TSystem.h>
82 
92 
101 
109 #include "Framework/Utils/AppInit.h"
110 #include "Framework/Utils/RunOpt.h"
112 
113 using std::string;
114 using std::vector;
115 using std::ostringstream;
116 
117 using namespace genie;
118 using namespace genie::hnl;
119 
120 #ifdef __GENIE_FLUX_DRIVERS_ENABLED__
121 #define __CAN_GENERATE_EVENTS_USING_A_FLUX__
122 //#include "Tools/Flux/GNuMIFlux.h"
123 #include <TH1.h>
124 #endif // #ifdef __GENIE_FLUX_DRIVERS_ENABLED__
125 
126 #ifdef __GENIE_GEOM_DRIVERS_ENABLED__
127 #define __CAN_USE_ROOT_GEOM__
128 #include <TGeoVolume.h>
129 #include <TGeoManager.h>
130 #include <TGeoShape.h>
131 #include <TGeoBBox.h>
132 #endif // #ifdef __GENIE_GEOM_DRIVERS_ENABLED__
133 
134 typedef enum t_HNLValidation {
135 
136  kValNone = 0,
139  kValGeom = 3,
141 
143 
144 // function prototypes
145 void GetCommandLineArgs (int argc, char ** argv);
146 void ReadInConfig (void);
147 void PrintSyntax (void);
148 const EventRecordVisitorI * HNLGenerator(void);
149 
150 #ifdef __CAN_GENERATE_EVENTS_USING_A_FLUX__
151 int TestFluxFromDk2nu (void);
152 #endif
153 
154 int TestDecay (void);
155 
156 #ifdef __CAN_USE_ROOT_GEOM__
157 int TestGeom (void);
158 void InitBoundingBox (void);
159 #endif // #ifdef __CAN_USE_ROOT_GEOM__
160 
161 //
162 string kDefOptGeomLUnits = "mm"; // default geometry length units
163 string kDefOptGeomAUnits = "rad"; // default geometry angle units
164 string kDefOptGeomTUnits = "ns"; // default geometry time units
165 string kDefOptGeomDUnits = "g_cm3"; // default geometry density units
166 
167 NtpMCFormat_t kDefOptNtpFormat = kNFGHEP; // default event tree format
168 string kDefOptEvFilePrefix = "gntp";
169 string kDefOptFluxFilePath = "./input-flux.root";
170 
171 string kDefOptSName = "genie::EventGenerator";
172 string kDefOptSConfig = "BeamHNL";
173 string kDefOptSTune = "GHNL20_00a_00_000";
174 
175 //
176 Long_t gOptRunNu = 1000; // run number
177 int gOptNev = 10; // number of events to generate
178 
180 
181 // ---- this section set in config
182 
183 double gCfgMassHNL = -1; // HNL mass
184 double gCfgECoupling = -1; // |U_e4|^2
185 double gCfgMCoupling = -1; // |U_m4|^2
186 double gCfgTCoupling = -1; // |U_t4|^2
187 bool gCfgIsMajorana = false;
188 int gCfgHNLKind = 2;
189 
190 double CoMLifetime = -1; // lifetime in centre-of-mass frame [GeV^-1]
191 
192 
193 HNLProd_t gCfgProdMode = kHNLProdNull; // HNL production mode
195 std::vector< HNLDecayMode_t > gCfgIntChannels; // decays to un-inhibit
196 
197 double gCfgUserOx = -1; // user origin x in beam coords
198 double gCfgUserOy = -1; // user origin y in beam coords
199 double gCfgUserOz = -1; // user origin z in beam coords
200 double gCfgUserAx1 = -1; // left-most extrinsic Euler angle (x)
201 double gCfgUserAz = -1; // middle extrinsic Euler angle (z)
202 double gCfgUserAx2 = -1; // right-most extrinsic Euler angle (x)
203 
204 double gCfgBoxLx = -1; // detector length on user x
205 double gCfgBoxLy = -1; // detector length on user y
206 double gCfgBoxLz = -1; // detector length on user z
207 
208 double gCfgParentEnergy = -1; // parent energy in GeV
209 double gCfgParentTheta = -1; // wrt beam axis
210 double gCfgParentPhi = -1; // 0 == x, pi/2 == y
211 double gCfgParentOx = -1; // user x of parent origin
212 double gCfgParentOy = -1; // user y of parent origin
213 double gCfgParentOz = -1; // user z of parent origin
214 
215 double gCfgHNLCx = -1; // directional cosine for x for HNL traj
216 double gCfgHNLCy = -1; // directional cosine for y for HNL traj
217 double gCfgHNLCz = -1; // directional cosine for z for HNL traj
218 
219 // ---- end config section
220 
221 double gOptEnergyHNL = -1; // HNL energy in GeV
222 
223 #ifdef __CAN_GENERATE_EVENTS_USING_A_FLUX__
224 string gOptFluxFilePath = kDefOptFluxFilePath; // where flux files live
225 bool gOptIsUsingDk2nu = false; // using flat dk2nu files?
226 #endif // #ifdef __CAN_GENERATE_EVENTS_USING_A_FLUX__
227 bool gOptIsMonoEnFlux = true; // do we have monoenergetic flux?
228 
229 string gOptEvFilePrefix = kDefOptEvFilePrefix; // event file prefix
230 bool gOptUsingRootGeom = false; // using root geom?
231 string gOptRootGeom; // input ROOT file with realistic detector geometry
232 
233 string geom;
235 double gOptGeomLUnits = 0; // input geometry length units
236 double gOptGeomAUnits = 0; // input geometry angle units
237 double gOptGeomTUnits = 0; // input geometry time units
238 #ifdef __CAN_USE_ROOT_GEOM__
239 TGeoManager * gOptRootGeoManager = 0; // the workhorse geometry manager
240 TGeoVolume * gOptRootGeoVolume = 0;
241 #endif // #ifdef __CAN_USE_ROOT_GEOM__
242 string gOptRootGeomTopVol = ""; // input geometry top event generation volume
243 
244 // Geometry bounding box and origin - read from the input geometry file (if any)
245 double fdx = 0; // half-length - x
246 double fdy = 0; // half-length - y
247 double fdz = 0; // half-length - z
248 double fox = 0; // origin - x
249 double foy = 0; // origin - y
250 double foz = 0; // origin - z
251 
252 long int gOptRanSeed = -1; // random number seed
253 
254 //_________________________________________________________________________________________
255 int main(int argc, char ** argv)
256 {
257  // suppress ROOT Info messages
258  gErrorIgnoreLevel = kWarning;
259 
260  // Parse command line arguments
261  GetCommandLineArgs(argc,argv);
262 
263  // Get the validation configuration
264  ReadInConfig();
265 
266  // Init messenger and random number seed
267  utils::app_init::MesgThresholds(RunOpt::Instance()->MesgThresholdFiles());
269 
270  switch( gOptValidationMode ){
271 
272  case kValFluxFromDk2nu: return TestFluxFromDk2nu(); break;
273  case kValDecay: return TestDecay(); break;
274  case kValGeom: return TestGeom(); break;
275  default: LOG( "gevald_hnl", pFATAL ) << "I didn't recognise this mode. Goodbye world!"; break;
276  }
277 
278  return 0;
279 }
280 //_________________________________________________________________________________________
281 //............................................................................
282 #ifdef __CAN_GENERATE_EVENTS_USING_A_FLUX__
283 int TestFluxFromDk2nu()
284 {
285  assert( !gOptIsMonoEnFlux && gOptIsUsingDk2nu );
286 
287  string foutName("test_flux_dk2nu.root");
288 
289  LOG( "gevald_hnl", pINFO )
290  << "\n\nTesting flux prediction from dk2nu input files."
291  << "\nWill produce 1 ROOT file (" << foutName << ") with:"
292  << "\n--> Energy spectra for the HNL (total and broken down by parent)"
293  << "\n--> Production vertex locations"
294  << "\n--> Counters for each production mode"
295  << "\n--> Spectrum of acceptance correction as function of parent boost factor"
296  << "\n--> Boost factor spectrum of parents broken down by type";
297 
298  const Algorithm * algFluxCreator = AlgFactory::Instance()->GetAlgorithm("genie::hnl::FluxCreator", "Default");
299 
300  const FluxCreator * fluxCreator = dynamic_cast< const FluxCreator * >( algFluxCreator );
301 
302  fluxCreator->SetInputFluxPath( gOptFluxFilePath );
303  bool geom_is_accessible = ! (gSystem->AccessPathName(gOptRootGeom.c_str()));
304  if (!geom_is_accessible) {
305  LOG("gevald_hnl", pFATAL)
306  << "The specified ROOT geometry doesn't exist! Initialization failed!";
307  exit(1);
308  }
309  fluxCreator->SetGeomFile( gOptRootGeom );
310 
311  int maxFluxEntries = -1;
312 
313  if( !gOptRootGeoManager ) gOptRootGeoManager = TGeoManager::Import(gOptRootGeom.c_str());
314 
315  TGeoVolume * top_volume = gOptRootGeoManager->GetTopVolume();
316  assert( top_volume );
317  TGeoShape * ts = top_volume->GetShape();
318  __attribute__((unused)) TGeoBBox * box = (TGeoBBox *)ts;
319 
320  TFile * fout = TFile::Open( foutName.c_str(), "RECREATE" );
321  TH1D hEAll, hEPion, hEKaon, hEMuon, hENeuk;
322  TH1D hPop, hImpwt;
323  TH1D hAcceptanceCorr, hAcceptance, hAcceptNoBCorr;
324  TH3D hProdVtxPos;
325  TH1D hCounters;
326  TH1D hBAll, hBPion, hBKaon, hBMuon, hBNeuk;
327  TH1D hParamSpace; // to store mass + couplings
328 
329  hEAll = TH1D( "hEAll", "HNL energy - all parents", 1000, 0., 100. );
330  hEPion = TH1D( "hEPion", "HNL energy - pion parent", 1000, 0., 100. );
331  hEKaon = TH1D( "hEKaon", "HNL energy - kaon parent", 1000, 0., 100. );
332  hEMuon = TH1D( "hEMuon", "HNL energy - muon parent", 1000, 0., 100. );
333  hENeuk = TH1D( "hENeuk", "HNL energy - neuk parent", 1000, 0., 100. );
334 
335  hPop = TH1D( "hPop", "HNL populations in energy bins", 1000, 0., 100. );
336  hImpwt = TH1D( "hImpwt", "HNL importance weights", 1000, 0., 100. );
337 
338  static const Double_t accbins[] = { 0.0, 2.5e-7, 5.0e-7, 7.5e-7, 1.0e-6, 2.5e-6, 5.0e-6, 7.5e-6, 1.0e-5, 2.5e-5, 5.0e-5, 7.5e-5, 1.0e-4, 2.5e-4, 5.0e-4, 7.5e-4, 1.0e-3, 2.5e-3, 5.0e-3, 7.5e-3, 1.0e-2, 2.5e-2, 5.0e-2, 7.5e-2, 1.0e-1, 2.5e-1, 5.0e-1, 7.5e-1, 1.0e+0, 2.5e+0, 5.0e+0, 7.5e+0, 1.0e+1 };
339  const Int_t naccbins = sizeof(accbins)/sizeof(accbins[0]) - 1;
340  hAcceptanceCorr = TH1D( "hAcceptanceCorr", "HNL acceptance correction", naccbins, accbins );
341  hAcceptance = TH1D( "hAcceptance", "HNL acceptance" , 200, 0.0, 100.0 );
342  hAcceptNoBCorr = TH1D( "hAcceptNoBCorr", "HNL SAA * accCorr" , 200, 0.0, 100.0 );
343 
344  hProdVtxPos = TH3D( "hProdVtxPos", "HNL production vertex (user coordinates, cm)",
345  200, -100., 100., 200, -100., 100., 1100, -110000., 0.);
346 
347  hCounters = TH1D( "hCounters", "HNL production channel counters", 11, 0, 11 );
348 
349  hBAll = TH1D( "hBAll", "Boost beta - all parents", 100, 0., 1. );
350  hBPion = TH1D( "hBPion", "Boost beta - pion parent", 100, 0., 1. );
351  hBKaon = TH1D( "hBKaon", "Boost beta - kaon parent", 100, 0., 1. );
352  hBMuon = TH1D( "hBMuon", "Boost beta - muon parent", 100, 0., 1. );
353  hBNeuk = TH1D( "hBNeuk", "Boost beta - neuk parent", 100, 0., 1. );
354 
355  hParamSpace = TH1D( "hParamSpace", "Parameter space", 5, 0., 5. );
356 
357  int parPDG;
358  TLorentzVector p4HNL;
359  TLorentzVector x4HNL;
360  int nPion2Muon = 0, nPion2Electron = 0, nKaon2Muon = 0,
361  nKaon2Electron = 0, nKaon3Muon = 0, nKaon3Electron = 0,
362  nNeuk3Muon = 0, nNeuk3Electron = 0, nMuon3Numu = 0,
363  nMuon3Nue = 0, nMuon3Nutau = 0;
364  double nPOT = 0;
365  double betaMag;
366  double accCorr;
367  double nimpwt; // hadroproduction importance weight
368 
369  int ievent = 0;
370  while(true)
371  {
372  if( gOptNev >= 10000 ){
373  if( ievent % (gOptNev / 1000) == 0 ){
374  int irat = ievent / (gOptNev / 1000);
375  std::cerr << Form("%2.2f", 0.1 * irat) << " % ( " << ievent << " / "
376  << gOptNev << " ) \r" << std::flush;
377  }
378  } else if( gOptNev >= 100 ) {
379  if( ievent % (gOptNev / 10) == 0 ){
380  int irat = ievent / ( gOptNev / 10 );
381  std::cerr << 10.0 * irat << " % " << " ( " << ievent
382  << " / " << gOptNev << " ) \r" << std::flush;
383  }
384  }
385 
386  if( ievent == gOptNev ) break;
387 
388  EventRecord * event = new EventRecord;
389  event->SetXSec( ievent ); // will be overridden, use as handy container
390 
391  fluxCreator->ProcessEventRecord(event);
392 
393  // fluxCreator->ProcessEventRecord now tells us how many entries there are
394  maxFluxEntries = fluxCreator->GetNFluxEntries();
395  if( gOptNev > maxFluxEntries ){
396  LOG( "gevgen_hnl", pWARN )
397  << "You have asked for " << gOptNev << " events, but only provided "
398  << maxFluxEntries << " flux entries. Truncating events to " << maxFluxEntries << ".";
399  gOptNev = maxFluxEntries;
400  }
401 
402  FluxContainer vgnmf = fluxCreator->RetrieveFluxInfo();
403  FluxContainer * gnmf = &vgnmf;
404 
405  // reject nonsense
406  if( gnmf->Ecm < 0 ){
407 
408  LOG( "gevald_hnl", pDEBUG )
409  << "Skipping nonsense for event " << ievent << " (was this parent too light?)";
410 
411  } else {
412  // now to make stuff from this... i.e. fill histos
413 
414  // first get the channel
415  int iChannel = gnmf->prodChan;
416  HNLProd_t pChannel = static_cast<HNLProd_t>(iChannel);
417  int typeMod = (gnmf->pdg > 0) ? 1 : -1;
418 
419  parPDG = gnmf->parPdg;
420 
421  if( parPDG == 0 || parPDG == -9999 ){ ievent++; continue; }
422 
423  double MPar = PDGLibrary::Instance()->Find( parPDG )->Mass();
424  /*
425  TVector3 p3par( gnmf->xpoint, gnmf->ypoint, gnmf->zpoint );
426  double EPar = std::sqrt( p3par.Mag2() + MPar*MPar );
427  */
428  //TLorentzVector p4par( p3par.Px(), p3par.Py(), p3par.Pz(), EPar );
429  TLorentzVector p4par = gnmf->parp4; // NEAR, GeV
430  double EPar = p4par.E();
431 
432  TVector3 boost_beta = p4par.BoostVector();
433  betaMag = boost_beta.Mag();
434 
435  p4HNL = gnmf->p4; // NEAR coords, GeV
436  TVector3 dkVtx = gnmf->startPoint; // NEAR coords, m
437  double delay = gnmf->delay; // ns
438 
439  x4HNL.SetXYZT( dkVtx.X() * units::m / units::cm,
440  dkVtx.Y() * units::m / units::cm,
441  dkVtx.Z() * units::m / units::cm,
442  delay );
443 
444  double acceptance = gnmf->acceptance; // full acceptance
445  double bCorr = gnmf->boostCorr; // boost correction
446  accCorr = gnmf->accCorr; // just the acceptance correction
447  nimpwt = gnmf->nimpwt;
448 
449  double gam = std::sqrt( 1.0 / ( 1.0 - betaMag * betaMag ) );
450  double gamStar = gnmf->Ecm / gCfgMassHNL;
451  double betaStar = std::sqrt( 1.0 - 1.0 / ( gamStar * gamStar ) );
452  double bigBeta = betaMag * gam / betaStar;
453 
454  double Ox = -1.0 * x4HNL.X();
455  double Oy = -1.0 * x4HNL.Y();
456  double Oz = -1.0 * x4HNL.Z();
457 
458  double rootArg = gam*gam*Oz*Oz -
459  (gam*gam - bigBeta*bigBeta)*(Oz*Oz - bigBeta*bigBeta*(Ox*Ox + Oy*Oy));
460 
461  double timelikeBit = ( rootArg >= 0.0 ) ? std::sqrt( rootArg ) / ( betaMag * gam * gam * gam ) : 0.0;
462 
463  double fullTerm = (1.0 - 1.0 / gam) * Oz / ( betaMag * gam ) - timelikeBit;
464  if( std::abs( fullTerm ) > 100.0 ) fullTerm *= 100.0 / std::abs( fullTerm );
465 
466  nPOT = 1.0; // = gnmf->norig;
467 
468  // fill the histos!
469  hEAll.Fill( p4HNL.E(), acceptance * nimpwt );
470  hProdVtxPos.Fill( x4HNL.X(), x4HNL.Y(), x4HNL.Z(), nimpwt );
471  hBAll.Fill( betaMag, nimpwt );
472 
473  hPop.Fill( p4HNL.E(), 1.0 );
474  hImpwt.Fill( p4HNL.E(), nimpwt );
475  hAcceptanceCorr.Fill( accCorr, nimpwt );
476  hAcceptance.Fill( p4HNL.E(), nimpwt * acceptance );
477  hAcceptNoBCorr.Fill( p4HNL.E(), nimpwt * acceptance / ( bCorr * bCorr ) );
478 
479  switch( parPDG ){
480  case kPdgPiP:
481  hEPion.Fill( p4HNL.E(), acceptance * nimpwt );
482  hBPion.Fill( betaMag, nimpwt );
483  break;
484  case kPdgKP:
485  hEKaon.Fill( p4HNL.E(), acceptance * nimpwt );
486  hBKaon.Fill( betaMag, nimpwt );
487  break;
488  case kPdgK0L:
489  hENeuk.Fill( p4HNL.E(), acceptance * nimpwt );
490  hBNeuk.Fill( betaMag, nimpwt );
491  break;
492  case kPdgMuon:
493  hEMuon.Fill( p4HNL.E(), acceptance * nimpwt );
494  hBMuon.Fill( betaMag, nimpwt );
495  break;
496  }
497 
498  LOG( "gevald_hnl", pDEBUG )
499  << " *** Output for event no " << ievent << "... ***"
500  << "\nparPDG = " << parPDG
501  << "\np4par = " << utils::print::P4AsString( &p4par ) << " [GeV] "
502  << "\nbetaVec = " << utils::print::Vec3AsString( &boost_beta )
503  << " : mag = " << betaMag
504  << "\np4HNL = " << utils::print::P4AsString( &p4HNL ) << " [GeV] "
505  << "\nx4HNL = " << utils::print::X4AsString( &x4HNL ) << " [cm]"
506  << "\nacceptance = " << acceptance << " : accCorr = " << accCorr
507  << "\nnimpwt = " << nimpwt
508  << "\nProduction channel = " << utils::hnl::ProdAsString( pChannel );
509 
510  } // if not nonsense
511 
512  // clean up
513  delete event;
514 
515  ievent++;
516  }
517 
518  // fill hCounters; each bin corresponds to the HNLProd_t with index iBin-1
519  hCounters.SetBinContent( 1, nPion2Muon );
520  hCounters.SetBinContent( 2, nPion2Electron );
521  hCounters.SetBinContent( 3, nKaon2Muon );
522  hCounters.SetBinContent( 4, nKaon2Electron );
523  hCounters.SetBinContent( 5, nKaon3Muon );
524  hCounters.SetBinContent( 6, nKaon3Electron );
525  hCounters.SetBinContent( 7, nNeuk3Muon );
526  hCounters.SetBinContent( 8, nNeuk3Electron );
527  hCounters.SetBinContent( 9, nMuon3Numu );
528  hCounters.SetBinContent( 10, nMuon3Nue );
529  hCounters.SetBinContent( 11, nMuon3Nutau );
530 
531  hParamSpace.SetBinContent( 1, 1000.0 * gCfgMassHNL ); // MeV
532  hParamSpace.SetBinContent( 2, gCfgECoupling );
533  hParamSpace.SetBinContent( 3, gCfgMCoupling );
534  hParamSpace.SetBinContent( 4, gCfgTCoupling );
535  hParamSpace.SetBinContent( 5, nPOT );
536 
537  fout->Write();
538  fout->Close();
539 
540  return 0;
541 }
542 //............................................................................
543 #endif // #ifdef __CAN_GENERATE_EVENTS_USING_A_FLUX_
544 //_________________________________________________________________________________________
545 int TestDecay(void)
546 {
547  string foutName("test_decay.root");
548 
549  TFile * fout = TFile::Open( foutName.c_str(), "RECREATE" );
550 
551  __attribute__((unused)) const EventRecordVisitorI * mcgen = HNLGenerator();
552  const Algorithm * algHNLGen = AlgFactory::Instance()->GetAlgorithm("genie::hnl::Decayer", "Default");
553  const Decayer * hnlgen = dynamic_cast< const Decayer * >( algHNLGen );
554 
555  bool geom_is_accessible = ! (gSystem->AccessPathName(gOptRootGeom.c_str()));
556  if (!geom_is_accessible) {
557  LOG("gevald_hnl", pFATAL)
558  << "The specified ROOT geometry doesn't exist! Initialization failed!";
559  exit(1);
560  }
561 
562  if( !gOptRootGeoManager ) gOptRootGeoManager = TGeoManager::Import(gOptRootGeom.c_str());
563 
564  TGeoVolume * top_volume = gOptRootGeoManager->GetTopVolume();
565  assert( top_volume );
566  TGeoShape * ts = top_volume->GetShape();
567  __attribute__((unused)) TGeoBBox * box = (TGeoBBox *)ts;
568 
569  LOG( "gevald_hnl", pDEBUG ) << "Imported box.";
570 
571  const Algorithm * algVtxGen = AlgFactory::Instance()->GetAlgorithm("genie::hnl::VertexGenerator", "Default");
572 
573  const VertexGenerator * vtxGen = dynamic_cast< const VertexGenerator * >( algVtxGen );
574  vtxGen->SetGeomFile( gOptRootGeom );
575 
576  SimpleHNL sh = SimpleHNL( "HNLInstance", 0, kPdgHNL, kPdgKP,
578  std::map< HNLDecayMode_t, double > valMap = sh.GetValidChannels();
579  //const double CoMLifetime = sh.GetCoMLifetime();
580 
581  assert( valMap.size() > 0 ); // must be able to decay to something!
582  assert( (*valMap.begin()).first == kHNLDcyNuNuNu );
583 
584  LOG( "gevald_hnl", pINFO )
585  << "\n\nTesting decay modes for the HNL."
586  << "\nWill process gOptNev = " << gOptNev << " x ( N_channels = " << valMap.size()
587  << " ) = " << valMap.size() * gOptNev << " events, 1 for each valid channel."
588  << "\nWill produce 1 ROOT file ( " << foutName << " ) with:"
589  << "\n--> Energy spectrum for the decay products for each channel"
590  << "\n--> Rates of each decay channel";
591 
592  // Set GHEP print level
593  GHepRecord::SetPrintLevel(RunOpt::Instance()->EventRecordPrintLevel());
594 
595  // first set the 4-momentum of the HNL
596  //gOptEnergyHNL = utils::hnl::GetCfgDouble( "HNL", "ParticleGun", "PG-Energy" );
597  gOptEnergyHNL = hnlgen->GetPGunEnergy();
598  double p3HNL = std::sqrt( gOptEnergyHNL * gOptEnergyHNL - gCfgMassHNL * gCfgMassHNL );
599  assert( p3HNL >= 0.0 );
600  TLorentzVector * p4HNL = new TLorentzVector( p3HNL * gCfgHNLCx,
601  p3HNL * gCfgHNLCy,
602  p3HNL * gCfgHNLCz, gOptEnergyHNL );
603 
604  LOG( "gevald_hnl", pDEBUG )
605  << "\nUsing HNL with 4-momentum " << utils::print::P4AsString( p4HNL );
606  sh.SetEnergy( gOptEnergyHNL );
607  sh.SetMomentumDirection( gCfgHNLCx, gCfgHNLCy, gCfgHNLCz );
608 
609  TLorentzVector * x4HNL = new TLorentzVector( 1.0, 2.0, 3.0, 0.0 ); // dummy
610 
611  // now build array with indices of valid decay modes for speedy access
613  //double validRates[10] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
614  std::map< HNLDecayMode_t, double >::iterator vmit = valMap.begin(); int modeIdx = 0;
615  std::ostringstream msts;
616  for( ; vmit != valMap.end(); ++vmit ){
617  validModes[ modeIdx ] = (*vmit).first;
618  //validRates[ modeIdx ] = (*vmit).second;
619  msts << "\n" << utils::hnl::AsString( (*vmit).first );
620  modeIdx++;
621  }
622 
623  LOG( "gevald_hnl", pDEBUG ) << "Here are the modes in order : " << msts.str();
624 
625  // declare histos
626  // hSpectrum[i][j]: i iterates over HNLDecayMode_t, j over FS particle in same order as event record
627  TH1D hSpectrum[10][3], hCMSpectrum[10][3], hRates;
628  TH1D hParamSpace = TH1D( "hParamSpace", "Parameter space", 5, 0., 5. );
629 
630  hParamSpace.SetBinContent( 1, 1000.0 * gCfgMassHNL ); // MeV
631  hParamSpace.SetBinContent( 2, gCfgECoupling );
632  hParamSpace.SetBinContent( 3, gCfgMCoupling );
633  hParamSpace.SetBinContent( 4, gCfgTCoupling );
634 
635  hRates = TH1D( "hRates", "Rates of HNL decay channels", 10, 0, 10 );
636 
637  std::string shortModes[10] = { "vvv", "vee", "vmue", "pi0v", "pie", "vmumu", "pimu", "pi0pi0v",
638  "pipi0e", "pipi0mu" };
639  std::string part0names[10] = { "v1", "v", "v", "pi0", "pi", "v", "pi", "pi01", "pi", "pi" };
640  std::string part1names[10] = { "v2", "e1", "mu", "v", "e", "mu1", "mu", "pi02", "pi0", "pi0" };
641  std::string part2names[10] = { "v3", "e2", "e", "None", "None", "mu2", "None", "v", "e", "mu" };
642  std::string partNames[3][10] = { part0names, part1names, part2names };
643  for( unsigned int iChan = 0; iChan < valMap.size(); iChan++ ){
644 
645  std::string shortMode = shortModes[iChan];
646 
647  for( Int_t iPart = 0; iPart < 3; iPart++ ){
648  std::string ParticleName = partNames[iPart][iChan];
649  if( strcmp( ParticleName.c_str(), "None" ) != 0 ){
650  hSpectrum[iChan][iPart] = TH1D( Form( "hSpectrum_%s_%s", shortMode.c_str(), ParticleName.c_str() ),
651  Form( "Fractional energy of particle: %s in decay: %s",
652  ParticleName.c_str(),
653  (utils::hnl::AsString( validModes[iChan] )).c_str() ),
654  100, 0., 1.0 );
655  hCMSpectrum[iChan][iPart] = TH1D( Form( "hCMSpectrum_%s_%s", shortMode.c_str(), ParticleName.c_str() ),
656  Form( "Rest frame energy of particle: %s in decay: %s",
657  ParticleName.c_str(),
658  (utils::hnl::AsString( validModes[iChan] )).c_str() ),
659  100, 0., gCfgMassHNL );
660  } // only declare histos of particles that exist in decay
661  // and are of allowed decays
662  }
663  }
664 
665  // fill hRates now
666  double rnununu = ( (*valMap.find( kHNLDcyNuNuNu )) ).second;
667  double rnuee = ( valMap.find( kHNLDcyNuEE ) != valMap.end() ) ? (*(valMap.find( kHNLDcyNuEE ))).second : 0.0;
668  double rnumue = ( valMap.find( kHNLDcyNuMuE ) != valMap.end() ) ? (*(valMap.find( kHNLDcyNuMuE ))).second : 0.0;
669  double rpi0nu = ( valMap.find( kHNLDcyPi0Nu ) != valMap.end() ) ? (*(valMap.find( kHNLDcyPi0Nu ))).second : 0.0;
670  double rpie = ( valMap.find( kHNLDcyPiE ) != valMap.end() ) ? (*(valMap.find( kHNLDcyPiE ))).second : 0.0;
671  double rnumumu = ( valMap.find( kHNLDcyNuMuMu ) != valMap.end() ) ? (*(valMap.find( kHNLDcyNuMuMu ))).second : 0.0;
672  double rpimu = ( valMap.find( kHNLDcyPiMu ) != valMap.end() ) ? (*(valMap.find( kHNLDcyPiMu ))).second : 0.0;
673  double rpi0pi0nu = ( valMap.find( kHNLDcyPi0Pi0Nu ) != valMap.end() ) ? (*(valMap.find( kHNLDcyPi0Pi0Nu ))).second : 0.0;
674  double rpipi0e = ( valMap.find( kHNLDcyPiPi0E ) != valMap.end() ) ? (*(valMap.find( kHNLDcyPiPi0E ))).second : 0.0;
675  double rpipi0mu = ( valMap.find( kHNLDcyPiPi0Mu ) != valMap.end() ) ? (*(valMap.find( kHNLDcyPiPi0Mu ))).second : 0.0;
676 
677  hRates.SetBinContent( 1, rpimu );
678  hRates.SetBinContent( 2, rpie );
679  hRates.SetBinContent( 3, rpi0nu );
680  hRates.SetBinContent( 4, rnununu );
681  hRates.SetBinContent( 5, rnumumu );
682  hRates.SetBinContent( 6, rnuee );
683  hRates.SetBinContent( 7, rnumue );
684  hRates.SetBinContent( 8, rpipi0e );
685  hRates.SetBinContent( 9, rpipi0mu );
686  hRates.SetBinContent( 10, rpi0pi0nu );
687 
688  int ievent = 0;
689  while( true ){
690 
691  if( gOptNev >= 10000 ){
692  if( ievent % (gOptNev / 1000) == 0 ){
693  int irat = ievent / (gOptNev / 1000);
694  std::cerr << Form("%2.2f", 0.1 * irat) << " % ( " << ievent << " / "
695  << gOptNev << " ) \r" << std::flush;
696  }
697  } else if( gOptNev >= 100 ) {
698  if( ievent % (gOptNev / 10) == 0 ){
699  int irat = ievent / ( gOptNev / 10 );
700  std::cerr << 10.0 * irat << " % " << " ( " << ievent
701  << " / " << gOptNev << " ) \r" << std::flush;
702  }
703  }
704 
705  if( ievent == gOptNev ){ std::cerr << " \n"; break; }
706 
707  ostringstream asts;
708  for( unsigned int iMode = 0; iMode < valMap.size(); iMode++ ){
709  if( ievent == 0 ){
710  asts
711  << "\nDecay mode " << iMode << " is " << utils::hnl::AsString( validModes[ iMode ] );
712  }
713 
714  // build an event
715  EventRecord * event = new EventRecord;
716  Interaction * interaction = Interaction::HNL( genie::kPdgHNL, gOptEnergyHNL, validModes[ iMode ] );
717  // set Particle(0) and a dummy vertex
718  GHepParticle ptHNL( kPdgHNL, kIStInitialState, -1, -1, -1, -1, *p4HNL, *x4HNL );
719  event->AddParticle( ptHNL );
720  interaction->InitStatePtr()->SetProbeP4( *p4HNL );
721  event->SetVertex( *x4HNL );
722 
723  event->SetProbability( CoMLifetime );
724 
725  event->AttachSummary( interaction );
726  LOG( "gevald_hnl", pDEBUG )
727  << "Simulating decay with mode "
728  << utils::hnl::AsString( (HNLDecayMode_t) interaction->ExclTag().DecayMode() );
729 
730  // simulate the decay
731  hnlgen->ProcessEventRecord( event );
732 
733  // now get a weight.
734  // = exp( - T_{box} / \tau_{HNL} ) = exp( - L_{box} / ( \beta_{HNL} \gamma_{HNL} c ) * h / \Gamma_{tot} )
735  // placing the HNL at a point configured by the user
736  std::vector< double > PGOrigin = hnlgen->GetPGunOrigin();
737  double ox = PGOrigin.at(0), oy = PGOrigin.at(1), oz = PGOrigin.at(2);
738  /*
739  double ox = utils::hnl::GetCfgDouble( "HNL", "ParticleGun", "PG-OriginX" );
740  double oy = utils::hnl::GetCfgDouble( "HNL", "ParticleGun", "PG-OriginY" );
741  double oz = utils::hnl::GetCfgDouble( "HNL", "ParticleGun", "PG-OriginZ" );
742  */
743  ox *= units::m / units::mm; oy *= units::m / units::mm; oz *= units::m / units::mm;
744  TVector3 startPoint( ox, oy, oz ); TVector3 entryPoint, exitPoint;
745  TLorentzVector tmpVtx( ox, oy, oz, 0.0 );
746  event->SetVertex( tmpVtx );
747 
748  vtxGen->ProcessEventRecord(event);
749 
750  LOG( "gevald_hnl", pDEBUG ) << *event;
751 
752  // now fill the histos!
753  double wgt = event->Weight();
754  hSpectrum[iMode][0].Fill( (event->Particle(1))->E() / gOptEnergyHNL, wgt );
755  hSpectrum[iMode][1].Fill( (event->Particle(2))->E() / gOptEnergyHNL, wgt );
756  if( event->Particle(3) ) hSpectrum[iMode][2].Fill( (event->Particle(3))->E() / gOptEnergyHNL, wgt );
757 
758  // let's also fill the CM spectra
759  // get particle 4-momenta and boost back to rest frame!
760  TLorentzVector * p4p1 = (event->Particle(1))->GetP4();
761  TLorentzVector * p4p2 = (event->Particle(2))->GetP4();
762  TLorentzVector * p4p3 = 0;
763  if( event->Particle(3) ) p4p3 = (event->Particle(3))->GetP4();
764 
765  TVector3 boostVec = p4HNL->BoostVector();
766 
767  p4p1->Boost( -boostVec );
768  p4p2->Boost( -boostVec );
769  if( p4p3 ) p4p3->Boost( -boostVec );
770 
771  hCMSpectrum[iMode][0].Fill( p4p1->E(), wgt );
772  hCMSpectrum[iMode][1].Fill( p4p2->E(), wgt );
773  if( p4p3 ) hCMSpectrum[iMode][2].Fill( p4p3->E(), wgt );
774 
775  // clean-up
776  delete event;
777 
778  } // loop over valid decay channels
779 
780  if( ievent == 0 ) LOG( "gevald_hnl", pDEBUG ) << asts.str();
781  LOG( "gevald_hnl", pDEBUG ) << "Finished event: " << ievent;
782 
783  ievent++;
784 
785  } // event loop
786 
787  fout->cd();
788  hParamSpace.Write();
789  hRates.Write();
790  for( unsigned int i = 0; i < valMap.size(); i++ ){
791  for( Int_t j = 0; j < 3; j++ ){
792  std::string ParticleName = partNames[j][i];
793  if( strcmp( ParticleName.c_str(), "None" ) != 0 ){
794  hSpectrum[i][j].Write();
795  hCMSpectrum[i][j].Write();
796  }
797  }
798  }
799  fout->Write();
800  fout->Close();
801 
802  delete p4HNL;
803  delete x4HNL;
804  return 0;
805 }
806 //............................................................................
807 #ifdef __CAN_USE_ROOT_GEOM__
808 //_________________________________________________________________________________________
809 int TestGeom(void)
810 {
811  string foutName("test_geom.root");
812 
813  TFile * fout = TFile::Open( foutName.c_str(), "RECREATE" );
814 
815  LOG( "gevald_hnl", pINFO )
816  << "\n\nTesting ROOT geometry for ROOT file " << gOptRootGeom
817  << "\nWill produce 1 ROOT file ( " << foutName << " ) with:"
818  << "\n--> Specified geometry"
819  << "\n--> TTree containing the following branch structure:"
820  << "\n |"
821  << "\n |---- start x,y,z [mm]"
822  << "\n |"
823  << "\n |---- HNL 4-momentum [GeV]"
824  << "\n |"
825  << "\n |---- did intersect detector?"
826  << "\n |"
827  << "\n |---- entry x,y,z [mm]"
828  << "\n |"
829  << "\n |---- exit x,y,z [mm]"
830  << "\n |"
831  << "\n |---- weight"
832  << "\n |"
833  << "\n |---- HNL lifetime (rest frame) [ns]"
834  << "\n |"
835  << "\n |---- HNL lifetime (lab frame) [ns]"
836  << "\n |"
837  << "\n |---- decay x,y,z [mm]"
838  << "\n--> Weight histogram"
839  << "\n--> Travel-length histogram"
840  << "\n"
841  << "\n(where same coordinate system as user's is used and weight == P(HNL decays in detector) )";
842 
843  double dev_start[3] = { -9999.9, -9999.9, -9999.9 };
844  double dev_sphere[2] = { -9999.9, -9999.9 };
845  double use_start[3] = { -9999.9, -9999.9, -9999.9 };
846  double use_entry[3] = { -9999.9, -9999.9, -9999.9 };
847  double use_exit[3] = { -9999.9, -9999.9, -9999.9 };
848  double use_decay[3] = { -9999.9, -9999.9, -9999.9 };
849  double use_momentum[4] = { -9999.9, -9999.9, -9999.9, -9999.9 };
850  double use_wgt = -9999.9;
851  double use_lifetime = -9999.9, use_CMlifetime = -9999.9;
852  bool didIntersectDet = false;
853 
854  TTree * outTree = new TTree( "outTree", "Trajectory information tree" );
855  outTree->Branch( "startPoint", use_start, "startPoint[3]/D" );
856  outTree->Branch( "fourMomentum", use_momentum, "fourMomentum[4]/D" );
857  outTree->Branch( "startDeviate", dev_start, "startDeviate[3]/D" );
858  outTree->Branch( "spherDeviate", dev_sphere, "spherDeviate[2]/D" );
859  outTree->Branch( "didIntersect", &didIntersectDet, "didIntersect/O" );
860  outTree->Branch( "entryPoint", use_entry, "entryPoint[3]/D" );
861  outTree->Branch( "exitPoint", use_exit, "exitPoint[3]/D" );
862  outTree->Branch( "decayPoint", use_decay, "decayPoint[3]/D" );
863  outTree->Branch( "weight", &use_wgt, "weight/D" );
864  outTree->Branch( "lifetime_LAB", &use_lifetime, "lifetime_LAB/D" );
865  outTree->Branch( "lifetime_CM", &use_CMlifetime, "lifetime_CM/D" );
866 
867  TH1D hWeight( "hWeight", "Log_{10} ( P( decay in detector ) )",
868  160, -15.0, 1.0 );
869  TH1D hLength( "hLength", "Length travelled in detector / max possible length in detector",
870  100, 0., 1.0 );
871 
872  bool geom_is_accessible = ! (gSystem->AccessPathName(gOptRootGeom.c_str()));
873  if (!geom_is_accessible) {
874  LOG("gevald_hnl", pFATAL)
875  << "The specified ROOT geometry doesn't exist! Initialization failed!";
876  exit(1);
877  }
878 
879  gOptRootGeoManager = TGeoManager::Import(gOptRootGeom.c_str());
880  TGeoVolume * top_volume = gOptRootGeoManager->GetTopVolume();
881  assert( top_volume );
882 
883  // Read geometry bounding box - for vertex position generation
884  InitBoundingBox();
885 
886  const Algorithm * algHNLGen = AlgFactory::Instance()->GetAlgorithm("genie::hnl::Decayer", "Default");
887  const Algorithm * algVtxGen = AlgFactory::Instance()->GetAlgorithm("genie::hnl::VertexGenerator", "Default");
888 
889  const Decayer * hnlgen = dynamic_cast< const Decayer * >( algHNLGen );
890  const VertexGenerator * vtxGen = dynamic_cast< const VertexGenerator * >( algVtxGen );
891  vtxGen->SetGeomFile( gOptRootGeom );
892 
893  // get SimpleHNL for lifetime
894  SimpleHNL sh = SimpleHNL( "HNLInstance", 0, kPdgHNL, kPdgKP,
896 
897  // first set the 4-momentum of the HNL
898  //gOptEnergyHNL = utils::hnl::GetCfgDouble( "HNL", "ParticleGun", "PG-Energy" );
899  gOptEnergyHNL = hnlgen->GetPGunEnergy();
900  double p3HNL = std::sqrt( gOptEnergyHNL * gOptEnergyHNL - gCfgMassHNL * gCfgMassHNL );
901  assert( p3HNL >= 0.0 );
902  TLorentzVector * p4HNL = new TLorentzVector( p3HNL * gCfgHNLCx,
903  p3HNL * gCfgHNLCy,
904  p3HNL * gCfgHNLCz, gOptEnergyHNL );
905 
906  LOG( "gevald_hnl", pDEBUG )
907  << "\nUsing HNL with 4-momentum " << utils::print::P4AsString( p4HNL );
908  sh.SetEnergy( gOptEnergyHNL );
910 
911  double betaMag = p4HNL->P() / p4HNL->E();
912  __attribute__((unused)) double gamma = std::sqrt( 1.0 / ( 1.0 - betaMag * betaMag ) );
913 
914  use_CMlifetime = CoMLifetime / ( units::ns * units::GeV );
915  //sh.GetCoMLifetime() / ( units::ns * units::GeV );
916  use_lifetime = sh.GetLifetime() / ( units::ns * units::GeV ); // ns
917 
918  std::vector< double > PGOrigin = hnlgen->GetPGunOrigin();
919  std::vector< double > PGDOrigin = hnlgen->GetPGunDOrigin();
920 
921  const double PGox = PGOrigin.at(0), PGoy = PGOrigin.at(1), PGoz = PGOrigin.at(2);
922  const double PGdx = PGDOrigin.at(0), PGdy = PGDOrigin.at(1), PGdz = PGDOrigin.at(2);
923 
924  /*
925  const double PGox = utils::hnl::GetCfgDouble( "HNL", "ParticleGun", "PG-OriginX" );
926  const double PGoy = utils::hnl::GetCfgDouble( "HNL", "ParticleGun", "PG-OriginY" );
927  const double PGoz = utils::hnl::GetCfgDouble( "HNL", "ParticleGun", "PG-OriginZ" ); // m
928 
929  const double PGdx = utils::hnl::GetCfgDouble( "HNL", "ParticleGun", "PG-OriginDX" );
930  const double PGdy = utils::hnl::GetCfgDouble( "HNL", "ParticleGun", "PG-OriginDY" );
931  const double PGdz = utils::hnl::GetCfgDouble( "HNL", "ParticleGun", "PG-OriginDZ" ); // m
932  */
933  assert( PGdx > 0.0 && PGdy > 0.0 && PGdz > 0.0 );
934 
935  double c2 = std::sqrt( std::pow( gCfgHNLCx, 2.0 ) + std::pow( gCfgHNLCy, 2.0 ) + std::pow( gCfgHNLCz, 2.0 ) );
936  const double PGcx = gCfgHNLCx / c2;
937  const double PGcy = gCfgHNLCy / c2;
938  const double PGcz = gCfgHNLCz / c2; // unit-normalised
939 
940  const double PGtheta = std::acos( PGcz );
941  const double PGphi = ( std::sin( PGtheta ) < controls::kASmallNum ) ? 0.0 :
942  ( PGcy >= 0.0 ) ? std::acos( PGcx / PGcz ) : 2.0 * constants::kPi - std::acos( PGcx / PGcz );
943 
944  std::vector< double > PGDeviation = hnlgen->GetPGunDeviation();
945  const double PGdtheta = PGDeviation.at(0) * constants::kPi / 180.0; // rad
946  const double PGdphi = PGDeviation.at(1) * constants::kPi / 180.0; // rad
947  /*
948  const double PGdtheta = utils::hnl::GetCfgDouble( "HNL", "ParticleGun", "PG-DTheta" ) * constants::kPi / 180.0;
949  const double PGdphi = utils::hnl::GetCfgDouble( "HNL", "ParticleGun", "PG-DPhi" ) * constants::kPi / 180.0; // rad
950  */
951 
952  /*
953  * The event loop works out a bit differently here.
954  * It reads in the origin point and momentum from file and treats them as CV
955  * Then it partitions each axis in R^3 (x-y-z, for origin position) and R^2 (theta-phi, for
956  * origin momentum) into either 5 points (x,y,z) or 9 points (theta, phi)
957  * There is exactly one trajectory that matches both origin position and momentum.
958  * For each of these trajectories, the entry and exit points are calculated.
959  * The length to decay is also calculated and a histo is filled with L(to decay) / L(max).
960  */
961 
962  // first make the points
963  const int NCARTESIAN = 5;
964  const int NSPHERICAL = 9;
965  const int NMAX = NCARTESIAN * NCARTESIAN * NCARTESIAN * NSPHERICAL * NSPHERICAL;
966 
967  double arr_ox[ NCARTESIAN ] = { PGox - PGdx, PGox - PGdx/2.0, PGox, PGox + PGdx/2.0, PGox + PGdx };
968  double arr_oy[ NCARTESIAN ] = { PGoy - PGdy, PGoy - PGdy/2.0, PGoy, PGoy + PGdy/2.0, PGoy + PGdy };
969  double arr_oz[ NCARTESIAN ] = { PGoz - PGdz, PGoz - PGdz/2.0, PGoz, PGoz + PGdz/2.0, PGoz + PGdz };
970 
971  double arr_theta[ NSPHERICAL ] = { PGtheta - PGdtheta, PGtheta - 3.0/4.0 * PGdtheta, PGtheta - 1.0/2.0 * PGdtheta, PGtheta - 1.0/4.0 * PGdtheta, PGtheta, PGtheta + 1.0/4.0 * PGdtheta, PGtheta + 1.0/2.0 * PGdtheta, PGtheta + 3.0/4.0 * PGdtheta, PGtheta + PGdtheta };
972  double arr_phi[ NSPHERICAL ] = { PGphi - PGdphi, PGphi - 3.0/4.0 * PGdphi, PGphi - 1.0/2.0 * PGdphi, PGphi - 1.0/4.0 * PGdphi, PGphi, PGphi + 1.0/4.0 * PGdphi, PGphi + 1.0/2.0 * PGdphi, PGphi + 3.0/4.0 * PGdphi, PGphi + PGdphi };
973 
974  // so now we have NCARTESIAN ^3 x NSPHERICAL ^2 points to iterate over. That's 10125 events for 5 and 9
975 
976  if( !gOptRootGeoManager ) gOptRootGeoManager = TGeoManager::Import(gOptRootGeom.c_str());
977 
978  TGeoShape * ts = top_volume->GetShape();
979  __attribute__((unused)) TGeoBBox * box = (TGeoBBox *)ts;
980 
981  int ievent = 0;
982  ostringstream asts;
983  while( true ){
984 
985  if( ievent == NMAX ) break;
986 
987  LOG( "gevald_hnl", pDEBUG )
988  << "*** Building event = " << ievent;
989 
990  EventRecord * event = new EventRecord;
992  event->SetProbability( CoMLifetime );
993  event->AttachSummary( interaction );
994 
995  /*
996  * Iterate over events as follows:
997  * Least significant --> most significant (with period in brackets)
998  * ox (NCART) --> oy (NCART) --> oz (NCART) --> phi (NSPHE) --> theta (NSPHE)
999  */
1000 
1001  int ix = ievent % NCARTESIAN;
1002  int iy = ( ievent / NCARTESIAN ) % NCARTESIAN;
1003  int iz = ( ievent / NCARTESIAN / NCARTESIAN ) % NCARTESIAN;
1004  int ip = ( ievent / NCARTESIAN / NCARTESIAN / NCARTESIAN ) % NSPHERICAL;
1005  int it = ( ievent / NCARTESIAN / NCARTESIAN / NCARTESIAN / NSPHERICAL ) % NSPHERICAL;
1006 
1007  double use_ox = arr_ox[ ix ] * units::m / units::mm;
1008  double use_oy = arr_oy[ iy ] * units::m / units::mm;
1009  double use_oz = arr_oz[ iz ] * units::m / units::mm;
1010 
1011  dev_start[0] = use_ox - PGox * units::m / units::mm;
1012  dev_start[1] = use_oy - PGoy * units::m / units::mm;
1013  dev_start[2] = use_oz - PGoz * units::m / units::mm;
1014 
1015  double use_theta = arr_theta[ it ];
1016  double use_phi = arr_phi[ ip ];
1017 
1018  dev_sphere[0] = (use_theta - PGtheta) * 180.0 / constants::kPi; // deg
1019  dev_sphere[1] = (use_phi - PGphi) * 180.0 / constants::kPi;
1020 
1021  double use_cx = std::cos( use_phi ) * std::sin( use_theta );
1022  double use_cy = std::sin( use_phi ) * std::sin( use_theta );
1023  double use_cz = std::cos( use_theta );
1024 
1025  // now, we set the correct start point and momentum.
1026  p4HNL->SetPxPyPzE( p3HNL * use_cx, p3HNL * use_cy, p3HNL * use_cz, gOptEnergyHNL );
1027 
1028  TVector3 startPoint, momentum;
1029  TVector3 entryPoint, exitPoint, decayPoint;
1030 
1031  startPoint.SetXYZ( use_ox, use_oy, use_oz );
1032  momentum.SetXYZ( p4HNL->Px(), p4HNL->Py(), p4HNL->Pz() );
1033 
1034  use_start[0] = use_ox;
1035  use_start[1] = use_oy;
1036  use_start[2] = use_oz;
1037 
1038  use_momentum[0] = p4HNL->Px();
1039  use_momentum[1] = p4HNL->Py();
1040  use_momentum[2] = p4HNL->Pz();
1041  use_momentum[3] = p4HNL->E();
1042 
1043  LOG( "gevald_hnl", pDEBUG )
1044  << "Set start point for this trajectory = " << utils::print::Vec3AsString( &startPoint )
1045  << " [mm]";
1046  LOG( "gevald_hnl", pDEBUG )
1047  << "Set momentum for this trajectory = " << utils::print::Vec3AsString( &momentum )
1048  << " [GeV/c]";
1049 
1050  TLorentzVector tmpVtx( use_ox, use_oy, use_oz, 0.0);
1051  event->SetVertex( tmpVtx );
1052  TLorentzVector tmpMom = *p4HNL;
1053  GHepParticle ptHNL( genie::kPdgHNL, kIStInitialState, -1, -1, -1, -1, tmpMom, tmpVtx );
1054  event->AddParticle( ptHNL );
1055  LOG( "gevald_hnl", pDEBUG )
1056  << "\nProbe p4 = " << utils::print::P4AsString( event->Particle(0)->P4() );
1057 
1058  vtxGen->ProcessEventRecord(event);
1059 
1060  if( event->Vertex()->T() != -999.9 ){
1061  decayPoint.SetXYZ( event->Vertex()->X(), event->Vertex()->Y(), event->Vertex()->Z() );
1062  entryPoint.SetXYZ( event->Particle(1)->Vx(), event->Particle(1)->Vy(), event->Particle(1)->Vz() );
1063  exitPoint.SetXYZ( event->Particle(2)->Vx(), event->Particle(2)->Vy(), event->Particle(2)->Vz() );
1064  use_wgt = event->Weight();
1065 
1066  // set the branch entries now
1067  use_entry[0] = entryPoint.X();
1068  use_entry[1] = entryPoint.Y();
1069  use_entry[2] = entryPoint.Z();
1070 
1071  use_exit[0] = exitPoint.X();
1072  use_exit[1] = exitPoint.Y();
1073  use_exit[2] = exitPoint.Z();
1074 
1075  use_decay[0] = decayPoint.X();
1076  use_decay[1] = decayPoint.Y();
1077  use_decay[2] = decayPoint.Z();
1078 
1079  double devX = decayPoint.X() * units::m / units::mm - entryPoint.X();
1080  double devY = decayPoint.Y() * units::m / units::mm - entryPoint.Y();
1081  double devZ = decayPoint.Z() * units::m / units::mm - entryPoint.Z();
1082 
1083  double mdeX = exitPoint.X() - entryPoint.X();
1084  double mdeY = exitPoint.Y() - entryPoint.Y();
1085  double mdeZ = exitPoint.Z() - entryPoint.Z();
1086 
1087  double elapsedLength = std::sqrt( devX * devX + devY * devY + devZ * devZ );
1088  double maxLength = std::sqrt( mdeX * mdeX + mdeY * mdeY + mdeZ * mdeZ );
1089 
1090  // also fill the histos
1091  hWeight.Fill( -1.0 * std::log10( use_wgt ), 1.0 );
1092  hLength.Fill( elapsedLength / maxLength );
1093  didIntersectDet = true;
1094 
1095  } else { // didn't intersect vertex, use nonsense
1096 
1097  use_entry[0] = -999.9;
1098  use_entry[1] = -999.9;
1099  use_entry[2] = -999.9;
1100 
1101  use_exit[0] = -999.9;
1102  use_exit[1] = -999.9;
1103  use_exit[2] = -999.9;
1104 
1105  use_decay[0] = -999.9;
1106  use_decay[1] = -999.9;
1107  use_decay[2] = -999.9;
1108 
1109  didIntersectDet = false;
1110  }
1111  outTree->Fill();
1112  ievent++;
1113  }
1114 
1115  // save to file and exit
1116 
1117  fout->cd();
1118  outTree->Write();
1119  hWeight.Write();
1120  hLength.Write();
1121  //top_volume->Write();
1122  fout->Close();
1123 
1124  return 0;
1125 }
1126 //_________________________________________________________________________________________
1127 void InitBoundingBox(void)
1128 {
1129 // Initialise geometry bounding box, used for generating HNL vertex positions
1130 
1131  LOG("gevald_hnl", pINFO)
1132  << "Initialising geometry bounding box.";
1133 
1134  fdx = 0; // half-length - x
1135  fdy = 0; // half-length - y
1136  fdz = 0; // half-length - z
1137  fox = 0; // origin - x
1138  foy = 0; // origin - y
1139  foz = 0; // origin - z
1140 
1141  if(!gOptUsingRootGeom) return;
1142 
1143  bool geom_is_accessible = ! (gSystem->AccessPathName(gOptRootGeom.c_str()));
1144  if (!geom_is_accessible) {
1145  LOG("gevald_hnl", pFATAL)
1146  << "The specified ROOT geometry doesn't exist! Initialization failed!";
1147  exit(1);
1148  }
1149 
1150  if( !gOptRootGeoManager ) gOptRootGeoManager = TGeoManager::Import(gOptRootGeom.c_str());
1151 
1152  TGeoVolume * top_volume = gOptRootGeoManager->GetTopVolume();
1153  assert( top_volume );
1154  TGeoShape * ts = top_volume->GetShape();
1155  TGeoBBox * box = (TGeoBBox *)ts;
1156 
1157  //get box origin and dimensions (in the same units as the geometry)
1158  fdx = box->GetDX();
1159  fdy = box->GetDY();
1160  fdz = box->GetDZ();
1161  fox = (box->GetOrigin())[0];
1162  foy = (box->GetOrigin())[1];
1163  foz = (box->GetOrigin())[2];
1164 
1165  LOG("gevald_hnl", pINFO)
1166  << "Before conversion the bounding box has:"
1167  << "\nOrigin = ( " << fox << " , " << foy << " , " << foz << " )"
1168  << "\nDimensions = " << fdx << " x " << fdy << " x " << fdz
1169  << "\n1cm = 1.0 unit";
1170 
1171  // Convert from local to SI units
1172  fdx *= gOptGeomLUnits;
1173  fdy *= gOptGeomLUnits;
1174  fdz *= gOptGeomLUnits;
1175  fox *= gOptGeomLUnits;
1176  foy *= gOptGeomLUnits;
1177  foz *= gOptGeomLUnits;
1178 
1179  LOG("gevald_hnl", pINFO)
1180  << "Initialised bounding box successfully.";
1181 
1182 }
1183 //_________________________________________________________________________________________
1184 #endif // #ifdef __CAN_USE_ROOT_GEOM__
1185 //............................................................................
1186 //_________________________________________________________________________________________
1187 const EventRecordVisitorI * HNLGenerator(void)
1188 {
1189  //string sname = "genie::EventGenerator";
1190  //string sconfig = "BeamHNL";
1191  AlgFactory * algf = AlgFactory::Instance();
1192 
1193  LOG("gevald_hnl", pINFO)
1194  << "Instantiating HNL generator.";
1195 
1196  const Algorithm * algmcgen = algf->GetAlgorithm(kDefOptSName, kDefOptSConfig);
1197  LOG("gevald_hnl", pDEBUG)
1198  << "Got algorithm " << kDefOptSName.c_str() << "/" << kDefOptSConfig.c_str();;
1199 
1200  const EventRecordVisitorI * mcgen =
1201  dynamic_cast< const EventRecordVisitorI * >( algmcgen );
1202  if(!mcgen) {
1203  LOG("gevald_hnl", pFATAL) << "Couldn't instantiate the HNL generator";
1204  gAbortingInErr = true;
1205  exit(1);
1206  }
1207 
1208  LOG("gevald_hnl", pINFO)
1209  << "HNL generator instantiated successfully.";
1210 
1211  return mcgen;
1212 }
1213 //_________________________________________________________________________________________
1214 void GetCommandLineArgs(int argc, char ** argv)
1215 {
1216  LOG("gevald_hnl", pINFO) << "Parsing command line arguments";
1217 
1218  // Parse run options for this app
1219 
1220  CmdLnArgParser parser(argc,argv);
1221 
1222  // help?
1223  bool help = parser.OptionExists('h');
1224  if(help) {
1225  PrintSyntax();
1226  exit(0);
1227  }
1228 
1229  // force the app to look at HNL tune by default
1230  // if user passes --tune argument, look at the user input tune instead
1231  char * expargv[ argc + 2 ];
1232  bool didFindUserInputTune = false;
1233  std::string stExtraTuneBit = kDefOptSTune;
1234 
1235  if( parser.OptionExists("tune") ){
1236  didFindUserInputTune = true;
1237  stExtraTuneBit = parser.ArgAsString("tune");
1238  LOG( "gevgen_hnl", pWARN )
1239  << "Using input HNL tune " << parser.ArgAsString("tune");
1240  } else {
1241  LOG( "gevgen_hnl", pWARN )
1242  << "Using default HNL tune " << kDefOptSTune;
1243  }
1244  // append this to argv
1245  for( int iArg = 0; iArg < argc; iArg++ ){
1246  expargv[iArg] = argv[iArg];
1247  }
1248  if( !didFindUserInputTune ){
1249  char * chBit = const_cast< char * >( stExtraTuneBit.c_str() ); // ugh. Ugly.
1250  std::string stune("--tune"); char * tBit = const_cast< char * >( stune.c_str() );
1251  expargv[argc] = tBit;
1252  expargv[argc+1] = chBit;
1253  }
1254 
1255  // Common run options.
1256  int expargc = ( didFindUserInputTune ) ? argc : argc+2;
1257  std::string stnull(""); char * nBit = const_cast< char * >( stnull.c_str() );
1258  expargv[expargc] = nBit;
1259 
1260  RunOpt::Instance()->ReadFromCommandLine(expargc,expargv);
1261 
1262  // run number
1263  if( parser.OptionExists('r') ) {
1264  LOG("gevald_hnl", pDEBUG) << "Reading MC run number";
1265  gOptRunNu = parser.ArgAsLong('r');
1266  } else {
1267  LOG("gevald_hnl", pDEBUG) << "Unspecified run number - Using default";
1268  gOptRunNu = 1000;
1269  } //-r
1270 
1271  // number of events
1272  if( parser.OptionExists('n') ) {
1273  LOG("gevald_hnl", pDEBUG)
1274  << "Reading number of events to generate";
1275  gOptNev = parser.ArgAsInt('n');
1276  } else {
1277  LOG("gevald_hnl", pFATAL)
1278  << "You need to specify the number of events";
1279  PrintSyntax();
1280  exit(0);
1281  } //-n
1282 
1283  if( parser.OptionExists('M') ) {
1284  LOG("gevald_hnl", pDEBUG)
1285  << "Detecting mode. . .";
1286  gOptValidationMode = (HNLValidation_t) parser.ArgAsInt('M');
1287  } else {
1288  LOG("gevald_hnl", pFATAL)
1289  << "You must specify a validation mode.";
1290  PrintSyntax();
1291  exit(0);
1292  } // -M
1293 
1294  bool isMonoEnergeticFlux = true;
1295 #ifdef __CAN_GENERATE_EVENTS_USING_A_FLUX__
1296  if( parser.OptionExists('f') ) {
1297  LOG("gevald_hnl", pDEBUG)
1298  << "A flux has been offered. Searching this path: " << parser.ArgAsString('f');
1299  isMonoEnergeticFlux = false;
1300  gOptFluxFilePath = parser.ArgAsString('f');
1301 
1302  // check if this is dk2nu
1303  //if( gOptFluxFilePath.find( "dk2nu" ) != string::npos ){
1304  if( gSystem->OpenDirectory( gOptFluxFilePath.c_str() ) != NULL ){
1305  gOptIsUsingDk2nu = true;
1306  LOG("gevald_hnl", pDEBUG)
1307  << "dk2nu flux files detected. Will create flux spectrum dynamically.";
1308  } else {
1309  LOG("gevald_hnl", pFATAL)
1310  << "Invalid flux file path " << gOptFluxFilePath;
1311  exit(1);
1312  }
1313  } else {
1314  // we need the 'E' option! Log it and pass below
1315  LOG("gevald_hnl", pINFO)
1316  << "No flux file offered. Assuming monoenergetic flux.";
1317  } //-f
1318  gOptIsMonoEnFlux = isMonoEnergeticFlux;
1319 #endif // #ifdef __CAN_GENERATE_EVENTS_USING_A_FLUX__
1320 
1321 #ifdef __CAN_USE_ROOT_GEOM__
1322  if( parser.OptionExists('g') ) {
1323  LOG("gevald_hnl", pDEBUG) << "Getting input geometry";
1324  geom = parser.ArgAsString('g');
1325 
1326  // is it a ROOT file that contains a ROOT geometry?
1327  bool accessible_geom_file =
1328  ! (gSystem->AccessPathName(geom.c_str()));
1329  if (accessible_geom_file) {
1330  gOptRootGeom = geom;
1331  gOptUsingRootGeom = true;
1332  } else {
1333  LOG("gevald_hnl", pFATAL)
1334  << "Geometry option is not a ROOT file. Please use ROOT geom.";
1335  PrintSyntax();
1336  exit(1);
1337  }
1338  } else if( gOptValidationMode == kValGeom ) {
1339 
1340  LOG("gevald_hnl", pFATAL)
1341  << "No geometry option specified - Exiting";
1342  PrintSyntax();
1343  exit(1);
1344  } //-g
1345 
1346  if( parser.OptionExists('L') ) {
1347  lunits = parser.ArgAsString('L');
1348  LOG("gevald_hnl", pDEBUG) << "Setting length units to " << lunits.c_str();
1349  } else {
1350  LOG("gevald_hnl", pDEBUG) << "Using default geometry length units";
1352  } // -L
1354 
1355  if( parser.OptionExists('A') ) {
1356  aunits = parser.ArgAsString('A');
1357  LOG("gevald_hnl", pDEBUG) << "Setting angle units to " << aunits.c_str();
1358  } else {
1359  LOG("gevald_hnl", pDEBUG) << "Using default angle length units";
1361  } // -A
1363 
1364  if( parser.OptionExists('T') ) {
1365  tunits = parser.ArgAsString('T');
1366  LOG("gevald_hnl", pDEBUG) << "Setting time units to " << tunits.c_str();
1367  } else {
1368  LOG("gevald_hnl", pDEBUG) << "Using default geometry time units";
1370  } // -T
1372 
1373 #endif // #ifdef __CAN_USE_ROOT_GEOM__
1374 
1375  // event file prefix
1376  if( parser.OptionExists('o') ) {
1377  LOG("gevald_hnl", pDEBUG) << "Reading the event filename prefix";
1378  gOptEvFilePrefix = parser.ArgAsString('o');
1379  } else {
1380  LOG("gevald_hnl", pDEBUG)
1381  << "Will set the default event filename prefix";
1383  } //-o
1384 
1385  // random number seed
1386  if( parser.OptionExists("seed") ) {
1387  LOG("gevald_hnl", pINFO) << "Reading random number seed";
1388  gOptRanSeed = parser.ArgAsLong("seed");
1389  } else {
1390  LOG("gevald_hnl", pINFO) << "Unspecified random number seed - Using default";
1391  gOptRanSeed = -1;
1392  }
1393 
1394  //
1395  // >>> print the command line options
1396  //
1397 
1398  ostringstream gminfo;
1399  if (gOptUsingRootGeom) {
1400  gminfo << "Using ROOT geometry - file: " << gOptRootGeom
1401  << ", top volume: "
1402  << ((gOptRootGeomTopVol.size()==0) ? "<master volume>" : gOptRootGeomTopVol)
1403  << ", length units: " << lunits;
1404  // << ", density units: " << dunits;
1405  }
1406 
1407  LOG("gevald_hnl", pNOTICE)
1408  << "\n\n"
1409  << utils::print::PrintFramedMesg("gevald_hnl job configuration");
1410 
1411  LOG("gevald_hnl", pNOTICE)
1412  << "\n @@ Run number : " << gOptRunNu
1413  << "\n @@ Random seed : " << gOptRanSeed
1414  << "\n @@ HNL mass : " << gCfgMassHNL << " GeV"
1415  << "\n @@ Decay channel : " << utils::hnl::AsString(gCfgDecayMode)
1416  << "\n @@ Flux path : " << gOptFluxFilePath
1417  << "\n @@ Geometry : " << gminfo.str()
1418  << "\n @@ Statistics : " << gOptNev << " events";
1419 }
1420 //_________________________________________________________________________________________
1421 void ReadInConfig(void)
1422 {
1423  LOG("gevald_hnl", pFATAL)
1424  << "Reading in validation configuration. . .";
1425 
1426  const Algorithm * algHNLGen = AlgFactory::Instance()->GetAlgorithm("genie::hnl::Decayer", "Default");
1427  __attribute__((unused)) const Decayer * hnlgen = dynamic_cast< const Decayer * >( algHNLGen );
1428 
1429  CoMLifetime = hnlgen->GetHNLLifetime();
1430 
1431  gCfgMassHNL = hnlgen->GetHNLMass();
1432  std::vector<double> U4l2s = hnlgen->GetHNLCouplings();
1433  gCfgECoupling = U4l2s.at(0);
1434  gCfgMCoupling = U4l2s.at(1);
1435  gCfgTCoupling = U4l2s.at(2);
1436  gCfgIsMajorana = hnlgen->IsHNLMajorana();
1437 
1438  //gOptEnergyHNL = utils::hnl::GetCfgDouble( "HNL", "ParticleGun", "PG-Energy" );
1439  gOptEnergyHNL = hnlgen->GetPGunEnergy();
1440 
1441  std::vector< double > PGDirection = hnlgen->GetPGunDirection();
1442  gCfgHNLCx = PGDirection.at(0), gCfgHNLCy = PGDirection.at(1), gCfgHNLCz = PGDirection.at(2);
1443  /*
1444  gCfgHNLCx = utils::hnl::GetCfgDouble( "HNL", "ParticleGun", "PG-cx" );
1445  gCfgHNLCy = utils::hnl::GetCfgDouble( "HNL", "ParticleGun", "PG-cy" );
1446  gCfgHNLCz = utils::hnl::GetCfgDouble( "HNL", "ParticleGun", "PG-cz" );
1447  */
1448 
1449  double dircosMag2 = std::pow( gCfgHNLCx, 2.0 ) +
1450  std::pow( gCfgHNLCy, 2.0 ) +
1451  std::pow( gCfgHNLCz, 2.0 );
1452  double invdircosmag = 1.0 / std::sqrt( dircosMag2 );
1453  gCfgHNLCx *= invdircosmag;
1454  gCfgHNLCy *= invdircosmag;
1455  gCfgHNLCz *= invdircosmag;
1456 
1457  std::string stIntChannels = hnlgen->GetHNLInterestingChannels(); int iChan = -1;
1458  if( gCfgIntChannels.size() > 0 ) gCfgIntChannels.clear();
1459  while( stIntChannels.size() > 0 ){ // read channels from right (lowest mass) to left (highest mass)
1460  iChan++;
1461  HNLDecayMode_t md = static_cast< HNLDecayMode_t >( iChan );
1462  std::string tmpSt = stIntChannels.substr( stIntChannels.size()-1, stIntChannels.size() );
1463  if( std::strcmp( tmpSt.c_str(), "1" ) == 0 )
1464  gCfgIntChannels.emplace_back( md );
1465 
1466  stIntChannels.erase( stIntChannels.end()-1, stIntChannels.end() );
1467  }
1468 
1469  const Algorithm * algFluxCreator = AlgFactory::Instance()->GetAlgorithm("genie::hnl::FluxCreator", "Default");
1470  __attribute__((unused)) const FluxCreator * fluxCreator = dynamic_cast< const FluxCreator * >( algFluxCreator );
1471 
1472  std::vector< double > b2uTranslation = fluxCreator->GetB2UTranslation();
1473  gCfgUserOx = b2uTranslation.at(0); gCfgUserOy = b2uTranslation.at(1); gCfgUserOz = b2uTranslation.at(2);
1474  std::vector< double > b2uRotation = fluxCreator->GetB2URotation();
1475  gCfgUserAx1 = b2uRotation.at(0); gCfgUserAz = b2uRotation.at(1); gCfgUserAx2 = b2uRotation.at(2);
1476 
1477  // now transform the lengths and angles to the correct units
1481 
1485 
1486  ostringstream csts;
1487  csts << "Read out the following config:"
1488  << "\n"
1489  << "\nHNL mass = " << gCfgMassHNL << " [GeV]"
1490  << "\n|U_e4|^2 = " << gCfgECoupling
1491  << "\n|U_m4|^2 = " << gCfgMCoupling
1492  << "\n|U_t4|^2 = " << gCfgTCoupling
1493  << "\n"
1494  << "\nInteresting decay channels:";
1495  for( std::vector< HNLDecayMode_t >::iterator chit = gCfgIntChannels.begin();
1496  chit != gCfgIntChannels.end(); ++chit ){ csts << "\n\t" << utils::hnl::AsString(*chit); }
1497  csts << "\n"
1498  << "\nUser origin in beam coordinates = ( " << gCfgUserOx
1499  << ", " << gCfgUserOy << ", " << gCfgUserOz << " ) [" << lunits.c_str() << "]"
1500  << "\nEuler extrinsic x-z-x rotation = ( " << gCfgUserAx1
1501  << ", " << gCfgUserAz << ", " << gCfgUserAx2 << " ) [" << aunits.c_str() << "]"
1502  << "\nHNL particle-gun directional cosines: ( " << gCfgHNLCx << ", " << gCfgHNLCy
1503  << ", " << gCfgHNLCz << ") [ GeV / GeV ]";
1504 
1505  LOG("gevald_hnl", pDEBUG) << csts.str();
1506 
1507 }
1508 //_________________________________________________________________________________________
1509 void PrintSyntax(void)
1510 {
1511  LOG("gevald_hnl", pFATAL)
1512  << "\n **Syntax**"
1513  << "\n gevald_hnl [-h] "
1514  << "\n [-r run#]"
1515  << "\n -n n_of_events"
1516  << "\n [-f path_to_flux_files]"
1517  << "\n [-g geometry_file]"
1518  << "\n -M mode:"
1519  << "\n 1: Flux prediction from dk2nu files. Needs -f option"
1520  << "\n 2: HNL decay validation. Specify an origin point and 4-momentum"
1521  << "\n in the \"ParticleGun\" section in config. Needs -g option."
1522  << "\n 3: Custom geometry file validation. Needs -g option"
1523  << "\n Specify origin, momentum, and wiggle room for both of these in the"
1524  << "\n \"ParticleGun\" section in config"
1525  << "\n Regardless of how many events you ask for, this will evaluate 125x81"
1526  << "\n events: 5^3 from wiggling origin and 9^2 from wiggling momentum direction"
1527  << "\n 4: Full simulation (like gevgen_hnl but with lots of debug!)"
1528  << "\n"
1529  << "\n The configuration file lives at $GENIE/config/CommonHNL.xml - see"
1530  << " <param_set name=\"Validation\">"
1531  << "\n"
1532  << "\n Please also read the detailed documentation at http://www.genie-mc.org"
1533  << "\n or look at the source code: $GENIE/src/Apps/gBeamHNLValidationApp.cxx"
1534  << "\n";
1535 }
1536 //_________________________________________________________________________________________
static constexpr double cm
Definition: Units.h:68
enum genie::hnl::t_HNLProd HNLProd_t
TLorentzVector parp4
parent momentum at HNL production in NEAR coords [GeV/c]
double gCfgBoxLz
double gCfgHNLCx
bool IsHNLMajorana() const
Definition: HNLDecayer.cxx:722
void RandGen(long int seed)
Definition: AppInit.cxx:30
static void SetPrintLevel(int print_level)
Definition: GHepRecord.cxx:956
double gOptGeomAUnits
double CoMLifetime
string aunits
virtual void SetXSec(double xsec)
Definition: GHepRecord.h:132
static constexpr double rad
Definition: Units.h:164
string kDefOptSTune
double gCfgParentOx
double Ecm
Parent rest-frame energy of HNL [GeV].
Heavy Neutral Lepton final-state product generator.
Definition: HNLDecayer.h:41
double gOptGeomTUnits
double gCfgUserOy
Heavy Neutral Lepton decay vertex generator given production vertex and momentum ***.
string gOptEvFilePrefix
Definition: gAtmoEvGen.cxx:310
void SetProbeP4(const TLorentzVector &P4)
double delay
delay HNL would have wrt SMv [ns]
double gCfgUserAx2
double gCfgParentEnergy
void ProcessEventRecord(GHepRecord *event_rec) const
string AsString(genie::hnl::HNLDecayMode_t hnldm)
Defines the EventRecordVisitorI interface. Concrete implementations of this interface use the &#39;Visito...
double gCfgUserOx
void ReadFromCommandLine(int argc, char **argv)
Definition: RunOpt.cxx:99
TVector3 startPoint
parent decay vertex in NEAR coords [m]
double foy
#define pFATAL
Definition: Messenger.h:56
double gCfgBoxLy
double gCfgParentPhi
int parPdg
parent PDG code
HNL object.
Definition: SimpleHNL.h:36
A GENIE flux container specific for HNL containers. Based on the dk2nu flux paradigm and genie::flux:...
string P4AsString(const TLorentzVector *p)
Definition: PrintUtils.cxx:27
std::vector< HNLDecayMode_t > gCfgIntChannels
Algorithm abstract base class.
Definition: Algorithm.h:54
string kDefOptGeomAUnits
static constexpr double ns
Definition: Units.h:98
double gCfgECoupling
HNLDecayMode_t gCfgDecayMode
double gCfgMCoupling
double gCfgUserAz
int main(int argc, char **argv)
Definition: gAtmoEvGen.cxx:327
double GetHNLMass() const
Definition: HNLDecayer.cxx:710
string kDefOptEvFilePrefix
Definition: gAtmoEvGen.cxx:320
int TestDecay(void)
Summary information for an interaction.
Definition: Interaction.h:56
string lunits
double GetPGunEnergy() const
Definition: HNLDecayer.cxx:750
double gCfgUserAx1
double fox
Calculates HNL production kinematics &amp; production vertex. Is a concrete implementation of the FluxRec...
int gCfgHNLKind
double fdy
#define LOG(stream, priority)
A macro that returns the requested log4cpp::Category appending a string (using the FILE...
Definition: Messenger.h:96
TLorentzVector p4
HNL momentum in NEAR coords [GeV/c].
void SetMomentumDirection(double ux, double uy, double uz)
Definition: SimpleHNL.h:231
string gOptRootGeomTopVol
Definition: gAtmoEvGen.cxx:301
void ProcessEventRecord(GHepRecord *event_rec) const
static constexpr double GeV
Definition: Units.h:28
double gOptEnergyHNL
std::vector< double > GetPGunDirection() const
Definition: HNLDecayer.cxx:755
bool gCfgIsMajorana
double GetHNLLifetime() const
Definition: HNLDecayer.cxx:705
string geom
double UnitFromString(string u)
Definition: UnitUtils.cxx:18
double gCfgParentOz
int prodChan
Decay mode that produced HNL.
string kDefOptSName
const Algorithm * GetAlgorithm(const AlgId &algid)
Definition: AlgFactory.cxx:75
int gOptNev
Definition: gAtmoEvGen.cxx:305
const int kPdgKP
Definition: PDGCodes.h:172
const int kPdgHNL
Definition: PDGCodes.h:224
int DecayMode(void) const
Definition: XclsTag.h:70
std::map< genie::hnl::HNLDecayMode_t, double > GetValidChannels()
Definition: SimpleHNL.h:154
const int kPdgPiP
Definition: PDGCodes.h:158
enum genie::hnl::EHNLDecayMode HNLDecayMode_t
static const double kASmallNum
Definition: Controls.h:40
double gCfgUserOz
FluxContainer RetrieveFluxInfo() const
string ProdAsString(genie::hnl::HNLProd_t hnlprod)
#define pINFO
Definition: Messenger.h:62
double gCfgHNLCz
const int kPdgK0L
Definition: PDGCodes.h:176
double acceptance
full acceptance == XYWgt * boostCorr^2 * accCorr
std::vector< double > GetPGunOrigin() const
Definition: HNLDecayer.cxx:736
string gOptRootGeom
Definition: gAtmoEvGen.cxx:300
#define pWARN
Definition: Messenger.h:60
std::vector< double > GetB2URotation() const
void SetEnergy(const double E)
Definition: SimpleHNL.h:183
double gCfgParentOy
double nimpwt
Weight of parent.
Long_t gOptRunNu
Definition: gAtmoEvGen.cxx:295
double gCfgHNLCy
std::string GetHNLInterestingChannels() const
Definition: HNLDecayer.cxx:727
Generated Event Record. It is a GHepRecord object that can accept / be visited by EventRecordVisitorI...
Definition: EventRecord.h:37
double accCorr
acceptance correction (collimation effect. SM v == 1)
string tunits
string kDefOptGeomLUnits
Definition: gAtmoEvGen.cxx:321
static PDGLibrary * Instance(void)
Definition: PDGLibrary.cxx:68
string kDefOptFluxFilePath
HNLProd_t gCfgProdMode
static RunOpt * Instance(void)
Definition: RunOpt.cxx:54
static AlgFactory * Instance()
Definition: AlgFactory.cxx:64
void ReadInConfig(void)
static __attribute__((unused)) double fDecayGammas[]
double gCfgMassHNL
std::vector< double > GetB2UTranslation() const
const XclsTag & ExclTag(void) const
Definition: Interaction.h:72
bool gOptIsMonoEnFlux
std::vector< double > GetHNLCouplings() const
Definition: HNLDecayer.cxx:715
double gCfgBoxLx
static constexpr double mm
Definition: Units.h:65
double fdx
string kDefOptSConfig
NtpMCFormat_t kDefOptNtpFormat
Definition: gAtmoEvGen.cxx:319
InitialState * InitStatePtr(void) const
Definition: Interaction.h:74
string PrintFramedMesg(string mesg, unsigned int nl=1, const char f='*')
Definition: PrintUtils.cxx:164
HNLValidation_t gOptValidationMode
void MesgThresholds(string inpfile)
Definition: AppInit.cxx:99
double foz
const EventRecordVisitorI * HNLGenerator(void)
TParticlePDG * Find(int pdgc, bool must_exist=true)
Definition: PDGLibrary.cxx:86
Command line argument parser.
enum genie::ENtpMCFormat NtpMCFormat_t
#define pNOTICE
Definition: Messenger.h:61
double boostCorr
boost correction wrt parent rest-frame (ELAB = ECM * boostCorr)
void GetCommandLineArgs(int argc, char **argv)
Definition: gAtmoEvGen.cxx:563
The GENIE Algorithm Factory.
Definition: AlgFactory.h:39
void ProcessEventRecord(GHepRecord *event) const
Definition: HNLDecayer.cxx:54
string X4AsString(const TLorentzVector *x)
Definition: PrintUtils.cxx:57
string kDefOptGeomTUnits
const int kPdgMuon
Definition: PDGCodes.h:37
string kDefOptGeomDUnits
Definition: gAtmoEvGen.cxx:322
void SetGeomFile(std::string geomfile) const
bool gAbortingInErr
Definition: Messenger.cxx:34
double gOptGeomLUnits
Definition: gAtmoEvGen.cxx:302
static constexpr double m
Definition: Units.h:71
Most commonly used PDG codes. A set of utility functions to handle PDG codes is provided in PDGUtils...
STDHEP-like event record entry that can fit a particle or a nucleus.
Definition: GHepParticle.h:39
string Vec3AsString(const TVector3 *vec)
Definition: PrintUtils.cxx:80
void PrintSyntax(void)
void SetInputFluxPath(std::string finpath) const
bool gOptUsingRootGeom
Definition: gAtmoEvGen.cxx:298
enum t_HNLValidation HNLValidation_t
double gCfgTCoupling
double gCfgParentTheta
double fdz
void SetGeomFile(string geomfile) const
#define pDEBUG
Definition: Messenger.h:63
static Interaction * HNL(int probe, double E=0, int decayed_mode=-1)
long int gOptRanSeed
Definition: gAtmoEvGen.cxx:312