GMCJDriver.cxx
Go to the documentation of this file.
1 //____________________________________________________________________________
2 /*
3  Copyright (c) 2003-2020, The GENIE Collaboration
4  For the full text of the license visit http://copyright.genie-mc.org
5 
6  Costas Andreopoulos <constantinos.andreopoulos \at cern.ch>
7  University of Liverpool & STFC Rutherford Appleton Laboratory
8 */
9 //____________________________________________________________________________
10 
11 #include <cassert>
12 
13 #include <TVector3.h>
14 #include <TSystem.h>
15 #include <TStopwatch.h>
16 
18 #include "Framework/Conventions/GBuild.h"
38 
39 using namespace genie;
40 using namespace genie::constants;
41 
42 //____________________________________________________________________________
44 {
45  this->InitJob();
46 }
47 //___________________________________________________________________________
49 {
50  if(fUnphysEventMask) delete fUnphysEventMask;
51  if (fGPool) delete fGPool;
52 
53  map<int,TH1D*>::iterator pmax_iter = fPmax.begin();
54  for( ; pmax_iter != fPmax.end(); ++pmax_iter) {
55  TH1D * pmax = pmax_iter->second;
56  if(pmax) {
57  delete pmax; pmax = 0;
58  }
59  }
60  fPmax.clear();
61 
62  if(fFluxIntTree) delete fFluxIntTree;
63  if(fFluxIntProbFile) delete fFluxIntProbFile;
64 }
65 //___________________________________________________________________________
66 void GMCJDriver::SetEventGeneratorList(string listname)
67 {
68  LOG("GMCJDriver", pNOTICE)
69  << "Setting event generator list: " << listname;
70 
71  fEventGenList = listname;
72 }
73 //___________________________________________________________________________
75 {
76  *fUnphysEventMask = mask;
77 
78  LOG("GMCJDriver", pNOTICE)
79  << "Setting unphysical event mask (bits: " << GHepFlags::NFlags() - 1
80  << " -> 0) : " << *fUnphysEventMask;
81 }
82 //___________________________________________________________________________
83 void GMCJDriver::UseFluxDriver(GFluxI * flux_driver)
84 {
85  fFluxDriver = flux_driver;
86 }
87 //___________________________________________________________________________
89 {
90  fGeomAnalyzer = geom_analyzer;
91 }
92 //___________________________________________________________________________
93 void GMCJDriver::UseSplines(bool useLogE)
94 {
95  fUseSplines = true;
96  fUseLogE = useLogE;
97 }
98 //___________________________________________________________________________
99 bool GMCJDriver::UseMaxPathLengths(string xml_filename)
100 {
101 // If you supply the maximum path lengths for all materials in your geometry
102 // (eg for ROOT/GEANT geometries they can be computed running GENIE's gmxpl
103 // application, see $GENIE/src/stdapp/gMaxPathLengths.cxx ) you can speed up
104 // the driver init phase by quite a bit (especially for complex geometries).
105 
106  fMaxPlXmlFilename = xml_filename;
107 
108  bool is_accessible = !(gSystem->AccessPathName(fMaxPlXmlFilename.c_str()));
109 
110  if ( is_accessible ) fUseExtMaxPl = true;
111  else {
112  fUseExtMaxPl = false;
113  LOG("GMCJDriver", pWARN)
114  << "UseMaxPathLengths could not find file: \"" << xml_filename << "\"";
115  }
116  return fUseExtMaxPl;
117 
118 }
119 //___________________________________________________________________________
121 {
122  LOG("GMCJDriver", pNOTICE)
123  << "Keep on throwing flux neutrinos till one interacts? : "
124  << utils::print::BoolAsYNString(keep_on);
125  fKeepThrowingFluxNu = keep_on;
126 }
127 //___________________________________________________________________________
129 {
130  fXSecSplineNbins = nbins;
131 
132  LOG("GMCJDriver", pNOTICE)
133  << "Number of bins in energy used for the xsec splines: "
134  << fXSecSplineNbins;
135 }
136 //___________________________________________________________________________
138 {
139  fPmaxLogBinning = true;
140 
141  LOG("GMCJDriver", pNOTICE)
142  << "Pmax will be store in histogram with logarithmic energy bins";
143 }
144 //___________________________________________________________________________
146 {
147  fPmaxNbins = nbins;
148 
149  LOG("GMCJDriver", pNOTICE)
150  << "Number of bins in energy used for maximum int. probability: "
151  << fPmaxNbins;
152 }
153 //___________________________________________________________________________
155 {
156  fPmaxSafetyFactor = sf;
157 
158  LOG("GMCJDriver", pNOTICE)
159  << "Pmax safety factor = " << fPmaxSafetyFactor;
160 }
161 //___________________________________________________________________________
163 {
164 // Force interaction in detector volume. That generates unweighted events.
165 //
166  fForceInteraction = true;
167 
168  LOG("GMCJDriver", pNOTICE)
169  << "GMCJDriver will generate weighted events forcing the interaction. ";
170 }
171 //___________________________________________________________________________
173 {
174 // Use a single probability scale. That generates unweighted events.
175 // (Note that generating unweighted event kinematics is a different thing)
176 //
177  fGenerateUnweighted = true;
178 
179  LOG("GMCJDriver", pNOTICE)
180  << "GMCJDriver will generate un-weighted events. "
181  << "Note: That does not force unweighted event kinematics!";
182 }
183 //___________________________________________________________________________
184 void GMCJDriver::PreSelectEvents(bool preselect)
185 {
186 // Set whether to pre-select events based on a max-path lengths file. This
187 // should be turned off if using pre-generated interaction probabilities
188 // calculated from a given flux file.
189  fPreSelect = preselect;
190 }
191 //___________________________________________________________________________
193 {
194 // Loop over complete set of flux entries satisfying input config options
195 // (such as neutrino type) and save the interaction probability in a tree
196 // relating flux index (entry number in input flux tree) to interaction
197 // probability. If a pre-generated flux interaction probability tree has
198 // already been loaded then just returns true. Also save tree to a TFile
199 // for use in later jobs if flag is set
200 //
201  bool success = true;
202 
203  bool save_to_file = fFluxIntProbFile == 0 && fFluxIntFileName.size()>0;
204 
205  // Clear map storing sum(fBrFluxWeight*fBrFluxIntProb) for each neutrino pdg
206  fSumFluxIntProbs.clear();
207 
208  // check if already loaded flux interaction probs using LoadFluxProbTree
209  if(fFluxIntTree){
210  LOG("GMCJDriver", pNOTICE) <<
211  "Skipping pre-generation of flux interaction probabilities - "<<
212  "using pre-generated file";
213  success = true;
214  }
215  // otherwise create them on the fly now
216  else {
217 
218  if(save_to_file){
219  fFluxIntProbFile = new TFile(fFluxIntFileName.c_str(), "CREATE");
220  if(fFluxIntProbFile->IsZombie()){
221  LOG("GMCJDriver", pFATAL) << "Cannot overwrite an existing file. Exiting!";
222  exit(1);
223  }
224  }
225 
226  // Create the tree to store flux probs
227  fFluxIntTree = new TTree(fFluxIntTreeName.c_str(),
228  "Tree storing pre-calculated flux interaction probs");
229  fFluxIntTree->Branch("FluxIndex", &fBrFluxIndex, "FluxIndex/I");
230  fFluxIntTree->Branch("FluxIntProb", &fBrFluxIntProb, "FluxIntProb/D");
231  fFluxIntTree->Branch("FluxEnu", &fBrFluxEnu, "FluxEnu/D");
232  fFluxIntTree->Branch("FluxWeight", &fBrFluxWeight, "FluxWeight/D");
233  fFluxIntTree->Branch("FluxPDG", &fBrFluxPDG, "FluxPDG/I");
234  // Associate to file otherwise get std::bad_alloc when writing large trees
235  if(save_to_file) fFluxIntTree->SetDirectory(fFluxIntProbFile);
236 
237  fFluxDriver->GenerateWeighted(true);
238 
239  fGlobPmax = 1.0; // Force ComputeInteractionProbabilities to return absolute value
240 
241  // Loop over flux entries and calculate interaction probabilities
242  TStopwatch stopwatch;
243  stopwatch.Start();
244  long int first_index = -1;
245  bool first_loop = true;
246  // loop until at end of flux ntuple
247  while(fFluxDriver->End() == false){
248 
249  // get the next flux neutrino
250  bool gotnext = fFluxDriver->GenerateNext();
251  if(!gotnext){
252  LOG("GMCJDriver", pWARN) << "*** Couldn't generate next flux ray! ";
253  continue;
254  }
255 
256  // stop if completed a full cycle (this check is necessary as fluxdriver
257  // may be set to loop over more than one cycle before reaching end)
258  bool already_been_here = first_loop ? false : first_index == fFluxDriver->Index();
259  if(already_been_here) break;
260 
261  // compute the path lengths for current flux neutrino
262  if(this->ComputePathLengths() == false){ success = false; break;}
263 
264  // compute and store the interaction probability
265  double psum = this->ComputeInteractionProbabilities(false /*Based on actual PLs*/);
266  assert(psum+controls::kASmallNum > 0.);
267  fBrFluxIntProb = psum;
268  fBrFluxIndex = fFluxDriver->Index();
269  fBrFluxEnu = fFluxDriver->Momentum().E();
270  fBrFluxWeight = fFluxDriver->Weight();
271  fBrFluxPDG = fFluxDriver->PdgCode();
272  fFluxIntTree->Fill();
273 
274  // store the first index so know when have cycled exactly once
275  if(first_loop){
276  first_index = fFluxDriver->Index();
277  first_loop = false;
278  }
279  } // flux loop
280  stopwatch.Stop();
281  LOG("GMCJDriver", pNOTICE)
282  << "Finished pre-calculating flux interaction probabilities. "
283  << "Total CPU time to process "<< fFluxIntTree->GetEntries()
284  << " entries: "<< stopwatch.CpuTime();
285 
286  // reset the flux driver so can be used at next stage. N.B. This
287  // should also reset flux driver to throw de-weighted flux neutrinos
288  fFluxDriver->Clear("CycleHistory");
289  }
290 
291  // If successfully calculated/loaded interaction probabilities then set global
292  // probability scale and, if requested, save tree to output file
293  if(success){
294  fGlobPmax = 0.0;
295  double safety_factor = 1.01;
296  for(int i = 0; i< fFluxIntTree->GetEntries(); i++){
297  fFluxIntTree->GetEntry(i);
298  // Check have non-negative probabilities
299  assert(fBrFluxIntProb+controls::kASmallNum > 0.0);
300  assert(fBrFluxWeight+controls::kASmallNum > 0.0);
301  // Update the global maximum
302  fGlobPmax = TMath::Max(fGlobPmax, fBrFluxIntProb*safety_factor);
303  // Update the sum of fBrFluxIntProb*fBrFluxWeight for different species
304  if(fSumFluxIntProbs.find(fBrFluxPDG) == fSumFluxIntProbs.end()){
305  fSumFluxIntProbs[fBrFluxPDG] = 0.0;
306  }
307  fSumFluxIntProbs[fBrFluxPDG] += fBrFluxIntProb * fBrFluxWeight;
308  }
309  LOG("GMCJDriver", pNOTICE) <<
310  "Updated global probability scale to fGlobPmax = "<< fGlobPmax;
311 
312  if(save_to_file){
313  LOG("GMCJDriver", pNOTICE) <<
314  "Saving pre-generated interaction probabilities to file: "<<
315  fFluxIntProbFile->GetName();
316  fFluxIntProbFile->cd();
317  fFluxIntTree->Write();
318  }
319 
320  // Also build index for use later
321  if(fFluxIntTree->BuildIndex("FluxIndex") != fFluxIntTree->GetEntries()){
322  LOG("GMCJDriver", pFATAL) <<
323  "Cannot build index using branch \"FluxIndex\" for flux prob tree!";
324  exit(1);
325  }
326 
327  // Now that have pre-generated flux probabilities need to trun off event
328  // preselection as this is only advantages when using max path lengths
329  this->PreSelectEvents(false);
330 
331  LOG("GMCJDriver", pNOTICE) << "Successfully generated/loaded pre-calculate flux interaction probabilities";
332  }
333  // Otherwise clean up
334  else if(fFluxIntTree){
335  delete fFluxIntTree;
336  fFluxIntTree = 0;
337  }
338 
339  // Return whether have successfully pre-calculated flux interaction probabilities
340  return success;
341 }
342 //___________________________________________________________________________
344 {
345 // Load a pre-generated set of flux interaction probabilities from an external
346 // file. This is recommended when using large flux files (>100k entries) as
347 // for these the time to calculate the interaction probabilities can exceed
348 // ~20 minutes. After loading the input tree we call PreCalcFluxProbabilities
349 // to check that has successfully loaded
350 //
351  if(fFluxIntProbFile){
352  LOG("GMCJDriver", pWARN)
353  << "Can't load flux interaction prob file as one is already loaded";
354  return false;
355  }
356 
357  fFluxIntProbFile = new TFile(filename.c_str(), "OPEN");
358 
359  if(fFluxIntProbFile){
360  fFluxIntTree = dynamic_cast<TTree*>(fFluxIntProbFile->Get(fFluxIntTreeName.c_str()));
361  if(fFluxIntTree){
362  bool set_addresses =
363  fFluxIntTree->SetBranchAddress("FluxIntProb", &fBrFluxIntProb) >= 0 &&
364  fFluxIntTree->SetBranchAddress("FluxIndex", &fBrFluxIndex) >= 0 &&
365  fFluxIntTree->SetBranchAddress("FluxPDG", &fBrFluxPDG) >= 0 &&
366  fFluxIntTree->SetBranchAddress("FluxWeight", &fBrFluxWeight) >= 0 &&
367  fFluxIntTree->SetBranchAddress("FluxEnu", &fBrFluxEnu) >= 0;
368  if(set_addresses){
369  // Finally check that can use them
370  if(this->PreCalcFluxProbabilities()) {
371  LOG("GMCJDriver", pNOTICE)
372  << "Successfully loaded pre-generated flux interaction probabilities";
373  return true;
374  }
375  }
376  // If cannot load then delete tree
377  LOG("GMCJDriver", pERROR) <<
378  "Cannot find expected branches in input flux probability tree!";
379  delete fFluxIntTree; fFluxIntTree = 0;
380  }
381  else LOG("GMCJDriver", pERROR)
382  << "Cannot find tree: "<< fFluxIntTreeName.c_str();
383  }
384 
385  LOG("GMCJDriver", pWARN)
386  << "Unable to load flux interaction probabilities file";
387  return false;
388 }
389 //___________________________________________________________________________
390 void GMCJDriver::SaveFluxProbabilities(string outfilename)
391 {
392 // Configue the flux driver to save the calculated flux interaction
393 // probabilities to the specified output file name for use in later jobs. See
394 // the LoadFluxProbTree method for how they are fed into a later job.
395 //
396  fFluxIntFileName = outfilename;
397 }
398 //___________________________________________________________________________
399 void GMCJDriver::Configure(bool calc_prob_scales)
400 {
401  LOG("GMCJDriver", pNOTICE)
402  << utils::print::PrintFramedMesg("Configuring GMCJDriver");
403 
404  // Get the list of neutrino types from the input flux driver and the list
405  // of target materials from the input geometry driver
406  this->GetParticleLists();
407 
408  // Ask the input GFluxI for the max. neutrino energy (to compute Pmax)
409  this->GetMaxFluxEnergy();
410 
411  // Create all possible initial states and for each one initialize,
412  // configure & store an GEVGDriver event generation driver object.
413  // Once an 'initial state' has been selected from the input flux / geom,
414  // the responsibility for generating the neutrino interaction will be
415  // delegated to one of these drivers.
416  this->PopulateEventGenDriverPool();
417 
418  // If the user wants to use cross section splines in order to speed things
419  // up, then coordinate spline creation from all GEVGDriver objects pushed
420  // into GEVGPool. This will create all xsec splines needed for all (enabled)
421  // processes that can be simulated involving the particles in the input flux
422  // and geometry.
423  // Spline creation will be skipped for every spline that has been pre-loaded
424  // into the the XSecSplineList.
425  // Once more it is noted that computing cross section splines is a huge
426  // overhead. The user is encouraged to generate them in advance and load
427  // them into the XSecSplineList
428  this->BootstrapXSecSplines();
429 
430  // Create cross section splines describing the total interaction xsec
431  // for a given initial state (Create them by summing all xsec splines
432  // for each possible initial state)
433  this->BootstrapXSecSplineSummation();
434 
435  if(calc_prob_scales && !fForceInteraction){
436  // Ask the input geometry driver to compute the max. path length for each
437  // material in the list of target materials (or load a precomputed list)
438  this->GetMaxPathLengthList();
439 
440  // Compute the max. interaction probability to scale all interaction
441  // probabilities to be computed by this driver
442  this->ComputeProbScales();
443  }
444  if (fForceInteraction) fGlobPmax = 1.;
445  LOG("GMCJDriver", pNOTICE) << "Finished configuring GMCJDriver\n\n";
446 }
447 //___________________________________________________________________________
449 {
450  fEventGenList = "Default"; // <-- set of event generators to be loaded by this driver
451 
452  fUnphysEventMask = new TBits(GHepFlags::NFlags()); //<-- unphysical event mask
453  //fUnphysEventMask->ResetAllBits(true);
454  for(unsigned int i = 0; i < GHepFlags::NFlags(); i++) {
455  fUnphysEventMask->SetBitNumber(i, true);
456  }
457 
458  fFluxDriver = 0; // <-- flux driver
459  fGeomAnalyzer = 0; // <-- geometry driver
460  fGPool = 0; // <-- pool of GEVGDriver event generation drivers
461  fEmax = 0; // <-- maximum neutrino energy
462  fMaxPlXmlFilename = ""; // <-- XML file with external path lengths
463  fUseExtMaxPl = false;
464  fUseSplines = false;
465  fNFluxNeutrinos = 0; // <-- number of flux neutrinos thrown so far
466 
467  fXSecSplineNbins = 100; // <-- number of energy bins used in the xsec splines
468  fPmaxLogBinning = false; // <-- maximum interaction probability is computed in logarithmic energy bins
469  fPmaxNbins = 300; // <-- number of energy bins used in the maximum interaction probability
470  fPmaxSafetyFactor = 1.2; // <-- safety factor to compute maximum interaction probability per neutrino & per energy bin
471  fGlobPmax = 0; // <-- maximum interaction probability (global prob scale)
472  fPmax.clear(); // <-- maximum interaction probability per neutrino & per energy bin
473 
474  fForceInteraction = false; // <-- default opt to not force the interaction
475  fGenerateUnweighted = false; // <-- default opt to generate weighted events
476  fPreSelect = true; // <-- default to use pre-selection based on maximum path lengths
477 
478  fSelTgtPdg = 0;
479  fCurEvt = 0;
480  fCurVtx.SetXYZT(0.,0.,0.,0.);
481 
482  fFluxIntProbFile = 0;
483  fFluxIntTreeName = "gFlxIntProb";
484  fFluxIntFileName = "";
485  fFluxIntTree = 0;
486  fBrFluxIntProb = -1.;
487  fBrFluxIndex = -1;
488  fBrFluxEnu = -1.;
489  fBrFluxWeight = -1.;
490  fBrFluxPDG = 0;
491  fSumFluxIntProbs.clear();
492 
493  // Throw as many flux neutrinos as necessary till one has interacted
494  // so that GenerateEvent() never returns NULL (except when in error)
495  this->KeepOnThrowingFluxNeutrinos(true);
496 
497  // Allow the selected GEVGDriver to go into recursive mode and regenerate
498  // an interaction that turns out to be unphysical.
499  //TBits unphysmask(GHepFlags::NFlags());
500  //unphysmask.ResetAllBits(false);
501  //this->FilterUnphysical(unphysmask);
502 
503  // Force early initialization of singleton objects that are typically
504  // would be initialized at their first use later on.
505  // This is purely cosmetic and I do it to send the banner and some prolific
506  // initialization printout at the top.
507  assert( Messenger::Instance() );
508  assert( AlgConfigPool::Instance() );
509 
510  // Clear the target and neutrino lists
511  fNuList.clear();
512  fTgtList.clear();
513 
514  // Clear the maximum path length list
515  fMaxPathLengths.clear();
516  fCurPathLengths.clear();
517 }
518 //___________________________________________________________________________
520 {
521  // Get the list of flux neutrinos from the flux driver
522  LOG("GMCJDriver", pNOTICE)
523  << "Asking the flux driver for its list of neutrinos";
524  fNuList = fFluxDriver->FluxParticles();
525 
526  LOG("GMCJDriver", pNOTICE) << "Flux particles: " << fNuList;
527 
528  // Get the list of target materials from the geometry driver
529  LOG("GMCJDriver", pNOTICE)
530  << "Asking the geometry driver for its list of targets";
531  fTgtList = fGeomAnalyzer->ListOfTargetNuclei();
532 
533  LOG("GMCJDriver", pNOTICE) << "Target materials: " << fTgtList;
534 }
535 //___________________________________________________________________________
537 {
538  if(fUseExtMaxPl) {
539  LOG("GMCJDriver", pNOTICE)
540  << "Loading external max path-length list for input geometry from "
541  << fMaxPlXmlFilename;
542  fMaxPathLengths.LoadFromXml(fMaxPlXmlFilename);
543 
544  } else {
545  LOG("GMCJDriver", pNOTICE)
546  << "Querying the geometry driver to compute the max path-length list";
547  fMaxPathLengths = fGeomAnalyzer->ComputeMaxPathLengths();
548  }
549  // Print maximum path lengths & neutrino energy
550  LOG("GMCJDriver", pNOTICE)
551  << "Maximum path length list: " << fMaxPathLengths;
552 }
553 //___________________________________________________________________________
555 {
556  LOG("GMCJDriver", pNOTICE)
557  << "Querying the flux driver for the maximum energy of flux neutrinos";
558  fEmax = fFluxDriver->MaxEnergy();
559 
560  LOG("GMCJDriver", pNOTICE)
561  << "Maximum flux neutrino energy = " << fEmax << " GeV";
562 }
563 //___________________________________________________________________________
565 {
566  LOG("GMCJDriver", pDEBUG)
567  << "Creating GEVGPool & adding a GEVGDriver object per init-state";
568 
569  if (fGPool) delete fGPool;
570  fGPool = new GEVGPool;
571 
574 
575  for(nuiter = fNuList.begin(); nuiter != fNuList.end(); ++nuiter) {
576  for(tgtiter = fTgtList.begin(); tgtiter != fTgtList.end(); ++tgtiter) {
577 
578  int target_pdgc = *tgtiter;
579  int neutrino_pdgc = *nuiter;
580 
581  InitialState init_state(target_pdgc, neutrino_pdgc);
582 
583  LOG("GMCJDriver", pNOTICE)
584  << "\n\n ---- Creating a GEVGDriver object configured for init-state: "
585  << init_state.AsString() << " ----\n\n";
586 
587  GEVGDriver * evgdriver = new GEVGDriver;
588  evgdriver->SetEventGeneratorList(fEventGenList); // specify list of generators
589  evgdriver->Configure(init_state);
590  evgdriver->UseSplines(); // check if all splines needed are loaded
591 
592  LOG("GMCJDriver", pDEBUG) << "Adding new GEVGDriver object to GEVGPool";
593  fGPool->insert( GEVGPool::value_type(init_state.AsString(), evgdriver) );
594  } // targets
595  } // neutrinos
596 
597  LOG("GMCJDriver", pNOTICE)
598  << "All necessary GEVGDriver object were pushed into GEVGPool\n";
599 }
600 //___________________________________________________________________________
602 {
603 // Bootstrap cross section spline generation by the event generation drivers
604 // that handle each initial state.
605 
606  if(!fUseSplines) return;
607 
608  LOG("GMCJDriver", pNOTICE)
609  << "Asking event generation drivers to compute all needed xsec splines";
610 
613  for(nuiter = fNuList.begin(); nuiter != fNuList.end(); ++nuiter){
614  for(tgtiter = fTgtList.begin(); tgtiter != fTgtList.end(); ++tgtiter) {
615  int target_pdgc = *tgtiter;
616  int neutrino_pdgc = *nuiter;
617  InitialState init_state(target_pdgc, neutrino_pdgc);
618  LOG("GMCJDriver", pINFO)
619  << "Computing all splines needed for init-state: "
620  << init_state.AsString();
621  GEVGDriver * evgdriver = fGPool->FindDriver(init_state);
622  evgdriver->CreateSplines(-1,-1,fUseLogE);
623  } // targets
624  } // neutrinos
625  LOG("GMCJDriver", pINFO) << "Finished creating cross section splines\n";
626 }
627 //___________________________________________________________________________
629 {
630 // Sum-up the cross section splines for all the interaction that can be
631 // simulated for each initial state
632 
633  LOG("GMCJDriver", pNOTICE)
634  << "Summing-up splines to get total cross section for each init state";
635 
636  GEVGPool::iterator diter;
637  for(diter = fGPool->begin(); diter != fGPool->end(); ++diter) {
638  string init_state = diter->first;
639  GEVGDriver * evgdriver = diter->second;
640  assert(evgdriver);
641  LOG("GMCJDriver", pNOTICE)
642  << "**** Summing xsec splines for init-state = " << init_state;
643 
644  Range1D_t rE = evgdriver->ValidEnergyRange();
645  if (fEmax>rE.max || fEmax<rE.min)
646  LOG("GMCJDriver",pFATAL)
647  << " rE (validEnergyRange) [" << rE.min << "," << rE.max << "] "
648  << " fEmax " << fEmax;
649  assert(fEmax<rE.max && fEmax>rE.min);
650 
651  // decide the energy range for the sum spline - extend the spline a little
652  // bit above the maximum beam energy (but below the maximum valid energy)
653  // to avoid the evaluation of the cubic spline around the viscinity of
654  // knots with zero y values (although the GENIE Spline object handles it)
655  double dE = fEmax/10.;
656  double min = rE.min;
657  double max = (fEmax+dE < rE.max) ? fEmax+dE : rE.max;
658 
659  // in the logaritmic binning is important to have a narrow binning to
660  // describe better the glashow resonance peak.
661  evgdriver->CreateXSecSumSpline(fXSecSplineNbins,min,max,true);
662  }
663  LOG("GMCJDriver", pNOTICE)
664  << "Finished summing all interaction xsec splines per initial state";
665 }
666 //___________________________________________________________________________
668 {
669 // Computing interaction probability scales.
670 // To minimize the numbers or trials before choosing a neutrino+target init
671 // state for generating an event (note: each trial means selecting a flux
672 // neutrino, navigating it through the detector to compute path lengths,
673 // computing interaction probabilities for each material and so on...)
674 // a set of probability scales can be used for different neutrino species
675 // and at different energy bins.
676 // A global probability scale is also being constructed for keeping the correct
677 // proportions between differect flux neutrino species or flux neutrinos of
678 // different energies.
679 
680  LOG("GMCJDriver", pNOTICE)
681  << "Computing the max. interaction probability (probability scale)";
682 
683  // clean up global probability scale and maximum probabilties per neutrino
684  // type & energy bin
685  {
686  fGlobPmax = 0;
687  map<int,TH1D*>::iterator pmax_iter = fPmax.begin();
688  for( ; pmax_iter != fPmax.end(); ++pmax_iter) {
689  TH1D * pmax = pmax_iter->second;
690  if(pmax) {
691  delete pmax; pmax = 0;
692  }
693  }
694  fPmax.clear();
695  }
696 
697  double * ebins;
698  if (fPmaxLogBinning) {
699  double emin = 0.1;
700  double de = (TMath::Log10(fEmax) - TMath::Log10(emin)) / fPmaxNbins;
701  ebins = new double[fPmaxNbins+1];
702  for(int i=0; i<=fPmaxNbins; i++) ebins[i] = TMath::Power(10., TMath::Log10(emin) + i * de);
703  }
704  else {
705  // for maximum interaction probability vs E /for given geometry/ I will
706  // be using 300 bins up to the maximum energy for the input flux
707  double de = fEmax/fPmaxNbins;//djk june 5, 2013
708  double emin = 0.0;
709  double emax = fEmax + de;
710  fPmaxNbins++;
711  ebins = new double[fPmaxNbins+1];
712  for(int i=0; i<=fPmaxNbins; i++) ebins[i] = emin + i * (emax-emin)/fPmaxNbins;
713  }
714 
717 
718  // loop over all neutrino types generated by the flux driver
719  for(nuiter = fNuList.begin(); nuiter != fNuList.end(); ++nuiter) {
720  int neutrino_pdgc = *nuiter;
721  TH1D * pmax_hst = new TH1D("pmax_hst",
722  "max interaction probability vs E | geom",fPmaxNbins,ebins);
723  pmax_hst->SetDirectory(0);
724 
725  // loop over energy bins
726  for(int ie = 1; ie <= pmax_hst->GetNbinsX(); ie++) {
727  double EvLow = pmax_hst->GetBinCenter(ie) - 0.5*pmax_hst->GetBinWidth(ie);
728  double EvHigh = pmax_hst->GetBinCenter(ie) + 0.5*pmax_hst->GetBinWidth(ie);
729  //double Ev = pmax_hst->GetBinCenter(ie);
730 
731  // loop over targets in input geometry, form initial state and compute
732  // the sum of maximum interaction probabilities at the current energy bin
733  //
734  for(tgtiter = fTgtList.begin(); tgtiter != fTgtList.end(); ++tgtiter) {
735  int target_pdgc = *tgtiter;
736 
737  InitialState init_state(target_pdgc, neutrino_pdgc);
738 
739  LOG("GMCJDriver", pDEBUG)
740  << "Computing Pmax for init-state: " << init_state.AsString() << " E from " << EvLow << "-" << EvHigh;
741 
742  // get the appropriate driver
743  GEVGDriver * evgdriver = fGPool->FindDriver(init_state);
744 
745  // get xsec sum over all modelled processes for given neutrino+target)
746  double sxsecLow = evgdriver->XSecSumSpline()->Evaluate(EvLow);
747  double sxsecHigh = evgdriver->XSecSumSpline()->Evaluate(EvHigh);
748 
749  // get max{path-length x density}
750  double plmax = fMaxPathLengths.PathLength(target_pdgc);
751 
752  // compute/store the max interaction probabiity (for given energy)
753  int A = pdg::IonPdgCodeToA(target_pdgc);
754  double pmaxLow = this->InteractionProbability(sxsecLow, plmax, A);
755  double pmaxHigh = this->InteractionProbability(sxsecHigh, plmax, A);
756 
757  double pmax = pmaxHigh;
758  if ( pmaxLow > pmaxHigh){
759  pmax = pmaxLow;
760  LOG("GMCJDriver", pWARN)
761  << "Lower energy neutrinos have a higher probability of interacting than those at higher energy."
762  << " pmaxLow(E=" << EvLow << ")=" << pmaxLow << " and " << " pmaxHigh(E=" << EvHigh << ")=" << pmaxHigh;
763  }
764 
765  pmax_hst->SetBinContent(ie, pmax_hst->GetBinContent(ie) + pmax);
766 
767  LOG("GMCJDriver", pDEBUG)
768  << "Pmax[" << init_state.AsString() << ", Ev from " << EvLow << "-" << EvHigh << "] = " << pmax;
769  } // targets
770 
771  pmax_hst->SetBinContent(ie, fPmaxSafetyFactor * pmax_hst->GetBinContent(ie));
772 
773  LOG("GMCJDriver", pINFO)
774  << "Pmax[nu=" << neutrino_pdgc << ", Ev from " << EvLow << "-" << EvHigh << "] = "
775  << pmax_hst->GetBinContent(ie);
776  } // E
777 
778  fPmax.insert(map<int,TH1D*>::value_type(neutrino_pdgc,pmax_hst));
779  } // nu
780 
781  delete ebins;
782 
783  // Compute global probability scale
784  // Sum Probabilities {
785  // all neutrinos, all targets, @ max path length, @ max energy}
786  //
787  {
788  for(nuiter = fNuList.begin(); nuiter != fNuList.end(); ++nuiter) {
789  int neutrino_pdgc = *nuiter;
790  map<int,TH1D*>::const_iterator pmax_iter = fPmax.find(neutrino_pdgc);
791  assert(pmax_iter != fPmax.end());
792  TH1D * pmax_hst = pmax_iter->second;
793  assert(pmax_hst);
794 // double pmax = pmax_hst->GetBinContent(pmax_hst->FindBin(fEmax));
795  double pmax = pmax_hst->GetMaximum();
796  assert(pmax>0);
797 // fGlobPmax += pmax;
798  fGlobPmax = TMath::Max(pmax, fGlobPmax); // ?;
799  }
800  LOG("GMCJDriver", pNOTICE) << "*** Probability scale = " << fGlobPmax;
801  }
802 }
803 //___________________________________________________________________________
805 {
806  fCurPathLengths.clear();
807  fCurEvt = 0;
808  fSelTgtPdg = 0;
809  fCurVtx.SetXYZT(0.,0.,0.,0.);
810 }
811 //___________________________________________________________________________
813 {
814  LOG("GMCJDriver", pNOTICE) << "Generating next event...";
815 
816  this->InitEventGeneration();
817 
818  while(1) {
819  bool flux_end = fFluxDriver->End();
820  if(flux_end) {
821  LOG("GMCJDriver", pNOTICE)
822  << "No more neutrinos can be thrown by the flux driver";
823  return 0;
824  }
825 
826  EventRecord * event = this->GenerateEvent1Try();
827  if(event) return event;
828 
829  if(fKeepThrowingFluxNu) {
830  LOG("GMCJDriver", pNOTICE)
831  << "Flux neutrino didn't interact - Trying the next one...";
832  continue;
833  }
834  break;
835  } // (w(1)
836 
837  LOG("GMCJDriver", pINFO) << "Returning NULL event!";
838  return 0;
839 }
840 //___________________________________________________________________________
842 {
843 // attempt generating a neutrino interaction by firing a single flux neutrino
844 //
845  RandomGen * rnd = RandomGen::Instance();
846 
847  double Pno=0, Psum=0;
848  double R = rnd->RndEvg().Rndm();
849  LOG("GMCJDriver", pDEBUG) << "Rndm [0,1] = " << R;
850 
851  // Generate a neutrino using the input GFluxI & get current pdgc/p4/x4
852  bool flux_ok = this->GenerateFluxNeutrino();
853  if(!flux_ok) {
854  LOG("GMCJDriver", pERROR)
855  << "** Rejecting current flux neutrino (flux driver err)";
856  return 0;
857  }
858 
859  if (fForceInteraction) {
860  bool pl_ok = this->ComputePathLengths();
861  if(!pl_ok) {
862  LOG("GMCJDriver", pFATAL) << "** Cannot calculate path lenths!";
863  exit(1);
864  }
865  if(fCurPathLengths.AreAllZero()) {
866  LOG("GMCJDriver", pNOTICE)
867  << "** Rejecting current flux neutrino (misses generation volume)";
868  return 0;
869  }
870  Psum = this->ComputeInteractionProbabilities(false);
871  LOG("GMCJDriver", pNOTICE)
872  << "The interaction probability is: " << Psum;
873  R *= Psum;
874  }
875  else {
876  // Compute the interaction probabilities assuming max. path lengths
877  // and decide whether the neutrino would interact --
878  // Many flux neutrinos should be rejected here, drastically reducing
879  // the number of neutrinos that I need to propagate through the
880  // actual detector geometry (this is skipped when using
881  // pre-calculated flux interaction probabilities)
882  if(fPreSelect) {
883  LOG("GMCJDriver", pNOTICE)
884  << "Computing interaction probabilities for max. path lengths";
885 
886  Psum = this->ComputeInteractionProbabilities(true /* <- max PL*/);
887  Pno = 1-Psum;
888  LOG("GMCJDriver", pNOTICE)
889  << "The no-interaction probability (max. path lengths) is: "
890  << 100*Pno << " %";
891  if(Pno<0.) {
892  LOG("GMCJDriver", pFATAL)
893  << "Negative no-interaction probability! (P = " << 100*Pno << " %)"
894  << " Particle E=" << fFluxDriver->Momentum().E() << " type=" << fFluxDriver->PdgCode() << "Psum=" << Psum;
895  gAbortingInErr=true;
896  exit(1);
897  }
898  if(R>=1-Pno) {
899  LOG("GMCJDriver", pNOTICE)
900  << "** Rejecting current flux neutrino";
901  return 0;
902  }
903  } // preselect
904 
905  bool pl_ok = false;
906 
907 
908  // If possible use pre-generated flux neutrino interaction probabilities
909  if(fFluxIntTree){
910  Psum = this->PreGenFluxInteractionProbability();
911  }
912  // Else compute them in the usual manner
913  else {
914  // Compute (pathLength x density x weight fraction) for all materials
915  // in the input geometry, for the neutrino generated by the flux driver
916  pl_ok = this->ComputePathLengths();
917  if(!pl_ok) {
918  LOG("GMCJDriver", pERROR)
919  << "** Rejecting current flux neutrino (err computing path-lengths)";
920  return 0;
921  }
922  if(fCurPathLengths.AreAllZero()) {
923  LOG("GMCJDriver", pNOTICE)
924  << "** Rejecting current flux neutrino (misses generation volume)";
925  return 0;
926  }
927  Psum = this->ComputeInteractionProbabilities(false /* <- actual PL */);
928  }
929 
930 
931  if(TMath::Abs(Psum) < controls::kASmallNum){
932  LOG("GMCJDriver", pNOTICE)
933  << "** Rejecting current flux neutrino (has null interaction probability)";
934  return 0;
935  }
936 
937  // Now decide whether the current neutrino interacts
938  Pno = 1-Psum;
939  LOG("GMCJDriver", pNOTICE)
940  << "The actual 'no interaction' probability is: " << 100*Pno << " %";
941  if(Pno<0.) {
942  LOG("GMCJDriver", pFATAL)
943  << "Negative no interactin probability! (P = " << 100*Pno << " %)";
944 
945  // print info about what caused the problem
946  int nupdg = fFluxDriver -> PdgCode ();
947  const TLorentzVector & nup4 = fFluxDriver -> Momentum ();
948  const TLorentzVector & nux4 = fFluxDriver -> Position ();
949 
950  LOG("GMCJDriver", pWARN)
951  << "\n [-] Problematic neutrino: "
952  << "\n |----o PDG-code : " << nupdg
953  << "\n |----o 4-momentum : " << utils::print::P4AsString(&nup4)
954  << "\n |----o 4-position : " << utils::print::X4AsString(&nux4)
955  << "\n Emax : " << fEmax;
956 
957  LOG("GMCJDriver", pWARN)
958  << "\n Problematic path lengths:" << fCurPathLengths;
959 
960  LOG("GMCJDriver", pWARN)
961  << "\n Maximum path lengths:" << fMaxPathLengths;
962 
963  exit(1);
964  }
965  if(R>=1-Pno) {
966  LOG("GMCJDriver", pNOTICE)
967  << "** Rejecting current flux neutrino";
968  return 0;
969  }
970 
971  //
972  // The flux neutrino interacts!
973  //
974 
975  // Calculate path lengths for first time and check potential mismatch if
976  // used pre-generated flux interaction probabilities
977  if(fFluxIntTree){
978  pl_ok = this->ComputePathLengths();
979  if(!pl_ok) {
980  LOG("GMCJDriver", pFATAL) << "** Cannot calculate path lenths!";
981  exit(1);
982  }
983  double Psum_curr = this->ComputeInteractionProbabilities(false /* <- actual PL */);
984  bool mismatch = TMath::Abs(Psum-Psum_curr) > controls::kASmallNum;
985  if(mismatch){
986  LOG("GMCJDriver", pFATAL) <<
987  "** Mismatch between pre-calculated and current interaction "<<
988  "probabilities!";
989  exit(1);
990  }
991  }
992  }
993 
994  // Select a target material
995  fSelTgtPdg = this->SelectTargetMaterial(R);
996  if(fSelTgtPdg==0) {
997  LOG("GMCJDriver", pERROR)
998  << "** Rejecting current flux neutrino (failed to select tgt!)";
999  return 0;
1000  }
1001 
1002  // Ask the GEVGDriver object to select and generate an interaction and
1003  // its kinematics for the selected initial state & neutrino 4-momentum
1004  this->GenerateEventKinematics();
1005  if(!fCurEvt) {
1006  LOG("GMCJDriver", pWARN)
1007  << "** Couldn't generate kinematics for selected interaction";
1008  return 0;
1009  }
1010 
1011  // Generate an 'interaction position' in the selected material (in the
1012  // detector coord system), along the direction of nup4 & set it
1013  this->GenerateVertexPosition();
1014 
1015  // Set the event probability (probability for this event to happen given
1016  // the detector setup & the selected flux neutrino)
1017  // Note for users:
1018  // The above probability is stored at GHepRecord::Probability()
1019  // For normalization purposes make sure that you take into account the
1020  // GHepRecord::Weight() -if event generation is weighted-, and
1021  // GFluxI::Weight() -if beam simulation is weighted-.
1022  if(fForceInteraction) {
1023  double weight = 1. - TMath::Exp(-Psum);
1024  fCurEvt->SetProbability(Psum);
1025  fCurEvt->SetWeight(weight * fCurEvt->Weight());
1026  }
1027  else {
1028  this->ComputeEventProbability();
1029  }
1030 
1031  return fCurEvt;
1032 }
1033 //___________________________________________________________________________
1035 {
1036 // Ask the neutrino flux driver to generate a flux neutrino and make sure
1037 // that things look ok...
1038 //
1039  LOG("GMCJDriver", pNOTICE) << "Generating a flux neutrino";
1040 
1041  bool ok = fFluxDriver->GenerateNext();
1042  if(!ok) {
1043  LOG("GMCJDriver", pERROR)
1044  << "*** The flux driver couldn't generate a flux neutrino!!";
1045  return false;
1046  }
1047 
1048  fNFluxNeutrinos++;
1049  int nupdg = fFluxDriver -> PdgCode ();
1050  const TLorentzVector & nup4 = fFluxDriver -> Momentum ();
1051  const TLorentzVector & nux4 = fFluxDriver -> Position ();
1052 
1053  LOG("GMCJDriver", pNOTICE)
1054  << "\n [-] Generated flux neutrino: "
1055  << "\n |----o PDG-code : " << nupdg
1056  << "\n |----o 4-momentum : " << utils::print::P4AsString(&nup4)
1057  << "\n |----o 4-position : " << utils::print::X4AsString(&nux4);
1058 
1059  if(nup4.Energy() > fEmax) {
1060  LOG("GMCJDriver", pFATAL)
1061  << "\n *** Flux driver error ***"
1062  << "\n Generated flux v with E = " << nup4.Energy() << " GeV"
1063  << "\n Max v energy (declared by flux driver) = " << fEmax << " GeV"
1064  << "\n My interaction probability scaling is invalidated!!";
1065  return false;
1066  }
1067  if(!fNuList.ExistsInPDGCodeList(nupdg)) {
1068  LOG("GMCJDriver", pFATAL)
1069  << "\n *** Flux driver error ***"
1070  << "\n Generated flux v with pdg = " << nupdg
1071  << "\n It does not belong to the declared list of flux neutrinos"
1072  << "\n I was not configured to handle this!!";
1073  return false;
1074  }
1075  return true;
1076 }
1077 //___________________________________________________________________________
1079 {
1080 // Ask the geometry driver to compute (pathLength x density x weight frac.)
1081 // for all detector materials for the neutrino generated by the flux driver
1082 // and make sure that things look ok...
1083 
1084  fCurPathLengths.clear();
1085 
1086  const TLorentzVector & nup4 = fFluxDriver -> Momentum ();
1087  const TLorentzVector & nux4 = fFluxDriver -> Position ();
1088 
1089  fCurPathLengths = fGeomAnalyzer->ComputePathLengths(nux4, nup4);
1090 
1091  LOG("GMCJDriver", pNOTICE) << fCurPathLengths;
1092 
1093  if(fCurPathLengths.size() == 0) {
1094  LOG("GMCJDriver", pFATAL)
1095  << "\n *** Geometry driver error ***"
1096  << "\n Got an empty PathLengthList - No material found in geometry?";
1097  return false;
1098  }
1099 
1100  if(fCurPathLengths.AreAllZero()) {
1101  LOG("GMCJDriver", pNOTICE)
1102  << "current flux v doesn't cross any geometry material...";
1103  }
1104  return true;
1105 }
1106 //___________________________________________________________________________
1107 double GMCJDriver::ComputeInteractionProbabilities(bool use_max_path_length)
1108 {
1109  LOG("GMCJDriver", pNOTICE)
1110  << "Computing relative interaction probabilities for each material";
1111 
1112  // current flux neutrino code & 4-p
1113  int nupdg = fFluxDriver->PdgCode();
1114  const TLorentzVector & nup4 = fFluxDriver->Momentum();
1115 
1116  fCurCumulProbMap.clear();
1117 
1118  const PathLengthList & path_length_list =
1119  (use_max_path_length) ? fMaxPathLengths : fCurPathLengths;
1120 
1121  double probsum=0;
1123 
1124  for(pliter = path_length_list.begin();
1125  pliter != path_length_list.end(); ++pliter) {
1126  int mpdg = pliter->first; // material PDG code
1127  double pl = pliter->second; // density x path-length
1128  int A = pdg::IonPdgCodeToA(mpdg);
1129  double xsec = 0.; // sum of xsecs for all modelled processes for given init state
1130  double prob = 0.; // interaction probability
1131  double probn = 0.; // normalized interaction probability
1132 
1133  // find the GEVGDriver object that is handling the current init state
1134  InitialState init_state(mpdg, nupdg);
1135  GEVGDriver * evgdriver = fGPool->FindDriver(init_state);
1136  if(!evgdriver) {
1137  LOG("GMCJDriver", pFATAL)
1138  << "\n * The MC Job driver isn't properly configured!"
1139  << "\n * No event generation driver could be found for init state: "
1140  << init_state.AsString();
1141  exit(1);
1142  }
1143  // compute the interaction xsec and probability (if path-length>0)
1144  if(pl>0.) {
1145  const Spline * totxsecspl = evgdriver->XSecSumSpline();
1146  if(!totxsecspl) {
1147  LOG("GMCJDriver", pFATAL)
1148  << "\n * The MC Job driver isn't properly configured!"
1149  << "\n * Couldn't retrieve total cross section spline for init state: "
1150  << init_state.AsString();
1151  exit(1);
1152  } else {
1153  xsec = totxsecspl->Evaluate( nup4.Energy() );
1154  }
1155  prob = this->InteractionProbability(xsec,pl,A);
1156  LOG("GMCJDriver", pDEBUG)
1157  << " (xsec, pl, A)=(" << xsec << "," << pl << "," << A << ")";
1158 
1159  // scale the interaction probability to the maximum one so as not
1160  // to have to throw few billions of flux neutrinos before getting
1161  // an interaction...
1162  double pmax = 0;
1163  if(fForceInteraction) pmax = 1.;
1164  else if(fGenerateUnweighted) pmax = fGlobPmax;
1165  else {
1166  map<int,TH1D*>::const_iterator pmax_iter = fPmax.find(nupdg);
1167  assert(pmax_iter != fPmax.end());
1168  TH1D * pmax_hst = pmax_iter->second;
1169  assert(pmax_hst);
1170  int ie = pmax_hst->FindBin(nup4.Energy());
1171  pmax = pmax_hst->GetBinContent(ie);
1172  }
1173  assert(pmax>0);
1174  LOG("GMCJDriver", pDEBUG)
1175  << "Pmax=" << pmax;
1176  probn = prob/pmax;
1177  }
1178 #ifdef __GENIE_LOW_LEVEL_MESG_ENABLED__
1179  LOG("GMCJDriver", pNOTICE)
1180  << "tgt: " << mpdg << " -> TotXSec = "
1181  << xsec/units::cm2 << " cm^2, Norm.Prob = " << 100*probn << "%";
1182 #endif
1183 
1184  probsum += probn;
1185  fCurCumulProbMap.insert(map<int,double>::value_type(mpdg,probsum));
1186  }
1187  return probsum;
1188 }
1189 //___________________________________________________________________________
1191 {
1192 // Pick a target material using the pre-computed interaction probabilities
1193 // for a flux neutrino that has already been determined that interacts
1194 
1195  LOG("GMCJDriver", pNOTICE) << "Selecting target material";
1196  int tgtpdg = 0;
1197  map<int,double>::const_iterator probiter = fCurCumulProbMap.begin();
1198  for( ; probiter != fCurCumulProbMap.end(); ++probiter) {
1199  double prob = probiter->second;
1200  if(R<prob) {
1201  tgtpdg = probiter->first;
1202  LOG("GMCJDriver", pNOTICE)
1203  << "Selected target material = " << tgtpdg;
1204  return tgtpdg;
1205  }
1206  }
1207  LOG("GMCJDriver", pERROR)
1208  << "Could not select target material for an interacting neutrino";
1209  return 0;
1210 }
1211 //___________________________________________________________________________
1213 {
1214  int nupdg = fFluxDriver->PdgCode();
1215  const TLorentzVector & nup4 = fFluxDriver->Momentum();
1216 
1217  // Find the GEVGDriver object that generates interactions for the
1218  // given initial state (neutrino + target)
1219  InitialState init_state(fSelTgtPdg, nupdg);
1220  GEVGDriver * evgdriver = fGPool->FindDriver(init_state);
1221  if(!evgdriver) {
1222  LOG("GMCJDriver", pFATAL)
1223  << "No GEVGDriver object for init state: " << init_state.AsString();
1224  exit(1);
1225  }
1226 
1227  // propagate current unphysical event mask
1228  evgdriver->SetUnphysEventMask(*fUnphysEventMask);
1229 
1230  // Ask the GEVGDriver object to select and generate an interaction for
1231  // the selected initial state & neutrino 4-momentum
1232  LOG("GMCJDriver", pNOTICE)
1233  << "Asking the selected GEVGDriver object to generate an event";
1234  fCurEvt = evgdriver->GenerateEvent(nup4);
1235 }
1236 //___________________________________________________________________________
1238 {
1239  // Generate an 'interaction position' in the selected material, along
1240  // the direction of nup4
1241  LOG("GMCJDriver", pNOTICE)
1242  << "Asking the geometry analyzer to generate a vertex";
1243 
1244  const TLorentzVector & p4 = fFluxDriver->Momentum ();
1245  const TLorentzVector & x4 = fFluxDriver->Position ();
1246 
1247  const TVector3 & vtx = fGeomAnalyzer->GenerateVertex(x4, p4, fSelTgtPdg);
1248 
1249  TVector3 origin(x4.X(), x4.Y(), x4.Z());
1250  origin-=vtx; // computes vector dr = origin - vtx
1251 
1252  double dL = origin.Mag();
1253  double v = p4.Beta() * kLightSpeed /(units::meter/units::second);
1254  double dt = dL/v;
1255 
1256  LOG("GMCJDriver", pNOTICE)
1257  << "|vtx - origin|: dL = " << dL << " m, dt = " << dt << " sec";
1258 
1259  fCurVtx.SetXYZT(vtx.x(), vtx.y(), vtx.z(), x4.T() + dt);
1260 
1261  fCurEvt->SetVertex(fCurVtx);
1262 }
1263 //___________________________________________________________________________
1265 {
1266 // Compute event probability for the given flux neutrino & detector geometry
1267 
1268  // get interaction cross section
1269  double xsec = fCurEvt->XSec();
1270 
1271  // get path length in detector along v direction for specified target material
1272  PathLengthList::const_iterator pliter = fCurPathLengths.find(fSelTgtPdg);
1273  double path_length = pliter->second;
1274 
1275  // get target material mass number
1276  int A = pdg::IonPdgCodeToA(fSelTgtPdg);
1277 
1278  // calculate interaction probability
1279  double P = this->InteractionProbability(xsec, path_length, A);
1280 
1281  //
1282  // get weight for selected event
1283  //
1284 
1285  GHepParticle * nu = fCurEvt->Probe();
1286  int nu_pdg = nu->Pdg();
1287  double Ev = nu->P4()->Energy();
1288 
1289  double weight = 1.0;
1290  if(!fGenerateUnweighted) {
1291  map<int,TH1D*>::const_iterator pmax_iter = fPmax.find(nu_pdg);
1292  assert(pmax_iter != fPmax.end());
1293  TH1D * pmax_hst = pmax_iter->second;
1294  assert(pmax_hst);
1295  double pmax = pmax_hst->GetBinContent(pmax_hst->FindBin(Ev));
1296  assert(pmax>0);
1297  weight = pmax/fGlobPmax;
1298  }
1299 
1300  // set probability & update weight
1301  fCurEvt->SetProbability(P);
1302  fCurEvt->SetWeight(weight * fCurEvt->Weight());
1303 }
1304 //___________________________________________________________________________
1305 double GMCJDriver::InteractionProbability(double xsec, double pL, int A)
1306 {
1307 // P = Na (Avogadro number, atoms/mole) *
1308 // 1/A (1/mass number, mole/gr) *
1309 // xsec (total interaction cross section, cm^2) *
1310 // pL (density-weighted path-length, gr/cm^2)
1311 //
1312  xsec = xsec / units::cm2;
1314 
1315  return kNA*(xsec*pL)/A;
1316 }
1317 //___________________________________________________________________________
1319 {
1320 // Return the pre-computed interaction probability for the current flux
1321 // neutrino index (entry number in flux file). Exit if not possible as
1322 // using meaningless interaction probability leads to incorrect physics
1323 //
1324  if(!fFluxIntTree){
1325  LOG("GMCJDriver", pERROR) <<
1326  "Cannot get pre-computed flux interaction probability as no tree!";
1327  exit(1);
1328  }
1329 
1330  assert(fFluxDriver->Index() >= 0); // Check trying to find meaningfull index
1331 
1332  // Check if can find relevant entry and no mismatch in energies -->
1333  // using correct pre-gen interaction prob file
1334  bool found_entry = fFluxIntTree->GetEntryWithIndex(fFluxDriver->Index()) > 0;
1335  bool enu_match = false;
1336  if(found_entry){
1337  double rel_err = fBrFluxEnu-fFluxDriver->Momentum().E();
1338  if(fBrFluxEnu > controls::kASmallNum) rel_err /= fBrFluxEnu;
1339  enu_match = TMath::Abs(rel_err)<controls::kASmallNum;
1340  if(enu_match == false){
1341  LOG("GMCJDriver", pERROR) <<
1342  "Mismatch between: Enu_curr = "<< fFluxDriver->Momentum().E() <<
1343  ", Enu_pre_gen = "<< fBrFluxEnu;
1344  }
1345  }
1346  else {
1347  LOG("GMCJDriver", pERROR) << "Cannot find flux entry in interaction prob tree!";
1348  }
1349 
1350  // Exit if not successful
1351  bool success = found_entry && enu_match;
1352  if(!success){
1353  LOG("GMCJDriver", pFATAL) <<
1354  "Cannot find pre-generated interaction probability! Check you "<<
1355  "are using the correct pre-generated interaction prob file " <<
1356  "generated using current flux input file with same input " <<
1357  "config (same geom TopVol, neutrino species list)";
1358  exit(1);
1359  }
1360  assert(fGlobPmax+controls::kASmallNum>0.0);
1361  return fBrFluxIntProb/fGlobPmax;
1362 }
1363 //___________________________________________________________________________
intermediate_table::iterator iterator
void CreateXSecSumSpline(int nk, double Emin, double Emax, bool inlogE=true)
Definition: GEVGDriver.cxx:440
void SetUnphysEventMask(const TBits &mask)
Definition: GMCJDriver.cxx:74
Basic constants.
void ForceInteraction(void)
Definition: GMCJDriver.cxx:162
void PopulateEventGenDriverPool(void)
Definition: GMCJDriver.cxx:564
bool PreCalcFluxProbabilities(void)
Definition: GMCJDriver.cxx:192
void InitJob(void)
Definition: GMCJDriver.cxx:448
THE MAIN GENIE PROJECT NAMESPACE
Definition: AlgCmp.h:25
#define pERROR
Definition: Messenger.h:59
void GetMaxPathLengthList(void)
Definition: GMCJDriver.cxx:536
static RandomGen * Instance()
Access instance.
Definition: RandomGen.cxx:71
void SetEventGeneratorList(string listname)
Definition: GMCJDriver.cxx:66
const TLorentzVector * P4(void) const
Definition: GHepParticle.h:78
static constexpr double gram
Definition: Units.h:140
void InitEventGeneration(void)
Definition: GMCJDriver.cxx:804
A simple [min,max] interval for doubles.
Definition: Range1.h:42
string BoolAsYNString(bool b)
Definition: PrintUtils.cxx:108
A numeric analysis tool class for interpolating 1-D functions.
Definition: Spline.h:46
#define pFATAL
Definition: Messenger.h:56
int IonPdgCodeToA(int pdgc)
Definition: PDGUtils.cxx:60
bool GenerateFluxNeutrino(void)
bool ComputePathLengths(void)
string P4AsString(const TLorentzVector *p)
Definition: PrintUtils.cxx:27
std::pair< float, std::string > P
intermediate_table::const_iterator const_iterator
const Spline * XSecSumSpline(void) const
Definition: GEVGDriver.h:87
double Evaluate(double x) const
Definition: Spline.cxx:361
string filename
Definition: train.py:213
A singleton holding random number generator classes. All random number generation in GENIE should tak...
Definition: RandomGen.h:29
void ComputeEventProbability(void)
weight
Definition: test.py:257
int Pdg(void) const
Definition: GHepParticle.h:63
Range1D_t ValidEnergyRange(void) const
Definition: GEVGDriver.cxx:668
void UseFluxDriver(GFluxI *flux)
Definition: GMCJDriver.cxx:83
Object to be filled with the neutrino path-length, for all detector geometry materials, when starting from a position x and travelling along the direction of the neutrino 4-momentum.
void KeepOnThrowingFluxNeutrinos(bool keep_on)
Definition: GMCJDriver.cxx:120
void GetMaxFluxEnergy(void)
Definition: GMCJDriver.cxx:554
static constexpr double second
Definition: Units.h:37
static unsigned int NFlags(void)
Definition: GHepFlags.h:76
int PdgCode(Resonance_t res, int Q)
(resonance id, charge) -> PDG code
#define LOG(stream, priority)
A macro that returns the requested log4cpp::Category appending a string (using the FILE...
Definition: Messenger.h:96
static constexpr double kilogram
Definition: Units.h:36
void SetEventGeneratorList(string listname)
Definition: GEVGDriver.cxx:348
static constexpr double m2
Definition: Units.h:72
void SetPmaxSafetyFactor(double sf)
Definition: GMCJDriver.cxx:154
static constexpr double cm2
Definition: Units.h:69
void ForceSingleProbScale(void)
Definition: GMCJDriver.cxx:172
double PreGenFluxInteractionProbability(void)
static Messenger * Instance(void)
Definition: Messenger.cxx:49
bool UseMaxPathLengths(string xml_filename)
Definition: GMCJDriver.cxx:99
void Configure(bool calc_prob_scales=true)
Definition: GMCJDriver.cxx:399
static const double kNA
Definition: Constants.h:49
string AsString(void) const
static const double kASmallNum
Definition: Controls.h:40
TRandom3 & RndEvg(void) const
rnd number generator used by the event generation drivers
Definition: RandomGen.h:74
GENIE Event Generation Driver. A minimalist user interface object for generating neutrino interaction...
Definition: GEVGDriver.h:54
#define pINFO
Definition: Messenger.h:62
void SetPmaxLogBinning(void)
Definition: GMCJDriver.cxx:137
static int max(int a, int b)
void BootstrapXSecSplines(void)
Definition: GMCJDriver.cxx:601
double ComputeInteractionProbabilities(bool use_max_path_length)
#define pWARN
Definition: Messenger.h:60
static const double kLightSpeed
Definition: Constants.h:31
EventRecord * GenerateEvent(void)
Definition: GMCJDriver.cxx:812
Generated Event Record. It is a GHepRecord object that can accept / be visited by EventRecordVisitorI...
Definition: EventRecord.h:37
double InteractionProbability(double xsec, double pl, int A)
void SetPmaxNbins(int nbins)
Definition: GMCJDriver.cxx:145
double max
Definition: Range1.h:53
void PreSelectEvents(bool preselect=true)
Definition: GMCJDriver.cxx:184
static constexpr double meter
Definition: Units.h:35
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
Definition: statistics.h:55
void GenerateEventKinematics(void)
int SelectTargetMaterial(double R)
void GenerateVertexPosition(void)
void SetXSecSplineNbins(int nbins)
Definition: GMCJDriver.cxx:128
void ComputeProbScales(void)
Definition: GMCJDriver.cxx:667
void Configure(int nu_pdgc, int Z, int A)
Definition: GEVGDriver.cxx:137
bool LoadFluxProbabilities(string filename)
Definition: GMCJDriver.cxx:343
EventRecord * GenerateEvent1Try(void)
Definition: GMCJDriver.cxx:841
#define A
Definition: memgrp.cpp:38
string PrintFramedMesg(string mesg, unsigned int nl=1, const char f='*')
Definition: PrintUtils.cxx:164
void CreateSplines(int nknots=-1, double emax=-1, bool inLogE=true)
Definition: GEVGDriver.cxx:577
double min
Definition: Range1.h:52
void BootstrapXSecSplineSummation(void)
Definition: GMCJDriver.cxx:628
void UseSplines(void)
Definition: GEVGDriver.cxx:508
#define pNOTICE
Definition: Messenger.h:61
Defines the GENIE Geometry Analyzer Interface.
Definition: GeomAnalyzerI.h:29
string X4AsString(const TLorentzVector *x)
Definition: PrintUtils.cxx:57
void SetUnphysEventMask(const TBits &mask)
Definition: GEVGDriver.cxx:219
bool gAbortingInErr
Definition: Messenger.cxx:34
STDHEP-like event record entry that can fit a particle or a nucleus.
Definition: GHepParticle.h:39
void UseGeomAnalyzer(GeomAnalyzerI *geom)
Definition: GMCJDriver.cxx:88
void UseSplines(bool useLogE=true)
Definition: GMCJDriver.cxx:93
void SaveFluxProbabilities(string outfilename)
Definition: GMCJDriver.cxx:390
constexpr Point origin()
Returns a origin position with a point of the specified type.
Definition: geo_vectors.h:227
void GetParticleLists(void)
Definition: GMCJDriver.cxx:519
Event finding and building.
static AlgConfigPool * Instance()
Initial State information.
Definition: InitialState.h:48
#define pDEBUG
Definition: Messenger.h:63
GENIE Interface for user-defined flux classes.
Definition: GFluxI.h:29
EventRecord * GenerateEvent(const TLorentzVector &nu4p)
Definition: GEVGDriver.cxx:228
A pool of GEVGDriver objects with an initial state key.
Definition: GEVGPool.h:37