All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MARLEYHelper.cxx
Go to the documentation of this file.
1 //////////////////////////////////////////////////////////////////////////////
2 /// \file MARLEYHelper.cxx
3 /// \brief LArSoft interface to the MARLEY (Model of Argon Reaction Low Energy
4 /// Yields) supernova neutrino event generator
5 ///
6 /// \author Steven Gardiner <sjgardiner@ucdavis.edu>
7 //////////////////////////////////////////////////////////////////////////////
8 
9 // framework includes
10 #include "cetlib_except/exception.h"
11 #include "messagefacility/MessageLogger/MessageLogger.h"
12 
13 // LArSoft includes
16 #include "nusimdata/SimulationBase/MCParticle.h"
17 #include "nurandom/RandomUtils/NuRandomService.h"
18 
19 // ROOT includes
20 #include "TInterpreter.h"
21 #include "TROOT.h"
22 
23 // MARLEY includes
24 #include "marley/Event.hh"
25 #include "marley/Particle.hh"
26 #include "marley/RootJSONConfig.hh"
27 
28 namespace {
29  // We need to convert from MARLEY's energy units (MeV) to LArSoft's
30  // (GeV) using this conversion factor
31  constexpr double MeV_to_GeV = 1e-3;
32 }
33 
34 //------------------------------------------------------------------------------
35 evgen::MARLEYHelper::MARLEYHelper( const fhicl::ParameterSet& pset,
36  rndm::NuRandomService& rand_service, const std::string& helper_name )
37  : fHelperName( helper_name )
38 {
39  // Configure MARLEY using the FHiCL parameters
40  this->reconfigure( pset );
41 
42  // Register this MARLEY generator with the NuRandomService. For simplicity,
43  // we use a lambda as the seeder function (see NuRandomService.h for
44  // details). This allows the SeedService to automatically re-seed MARLEY
45  // whenever necessary. The user can set an explicit seed for MARLEY in the
46  // FHiCL configuration using the "seed" parameter. If you need to get the
47  // seed for MARLEY from the SeedService, note that we're using use the value
48  // of the input variable helper_name as its generator instance name.
49  rndm::NuRandomService::seed_t marley_seed = rand_service.registerEngine(
50  [this](rndm::NuRandomService::EngineId const& /* unused */,
51  rndm::NuRandomService::seed_t lar_seed) -> void
52  {
53  if ( fMarleyGenerator && fMarleyGenerator.get() ) {
54  auto seed = static_cast<uint_fast64_t>( lar_seed );
55  fMarleyGenerator->reseed( seed );
56  }
57  },
58  fHelperName, pset, { "seed" }
59  );
60 
61  // Unless I'm mistaken, the call to registerEngine should seed the generator
62  // with the seed from the FHiCL configuration file if one is included, but it
63  // doesn't appear to do so (as of 16 Aug 2016, larsoft v06_03_00). As a
64  // workaround, I manually reseed the generator (if needed) here using the
65  // result of the call to registerEngine, which will be the seed from the
66  // FHiCL file if one was given.
67  // TODO: figure out what's going on here, and remove this workaround as
68  // needed
69  uint_fast64_t marley_cast_seed = static_cast<uint_fast64_t>( marley_seed );
70  if ( marley_cast_seed != fMarleyGenerator->get_seed() ) {
71  fMarleyGenerator->reseed( marley_cast_seed );
72  }
73 
74  // Log initialization information from the MARLEY generator
75  MF_LOG_INFO( fHelperName ) << fMarleyLogStream.str();
76  fMarleyLogStream = std::stringstream();
77 
78  // Do any needed setup of the MARLEY class dictionaries
80 }
81 
82 //------------------------------------------------------------------------------
83 void evgen::MARLEYHelper::add_marley_particles( simb::MCTruth& truth,
84  const std::vector<marley::Particle*>& particles,
85  const TLorentzVector& vtx_pos, bool track )
86 {
87  // Loop over the vector of MARLEY particles and add simb::MCParticle
88  // versions of each of them to the MCTruth object.
89  for ( const marley::Particle* p : particles ) {
90  // Treat all of these particles as primaries, which have negative
91  // track IDs by convention
92  int trackID = -1 * ( truth.NParticles() + 1 );
93 
94  int pdg = p->pdg_code();
95  double mass = p->mass() * MeV_to_GeV;
96  double px = p->px() * MeV_to_GeV;
97  double py = p->py() * MeV_to_GeV;
98  double pz = p->pz() * MeV_to_GeV;
99  double E = p->total_energy() * MeV_to_GeV;
100  TLorentzVector mom( px, py, pz, E );
101 
102  int status = 0; // don't track the particles in LArG4 by default
103  if ( track ) status = 1;
104 
105  simb::MCParticle part( trackID /* trackID to use in Geant4 */, pdg,
106  "MARLEY", -1 /* primary particle */, mass, status );
107 
108  part.AddTrajectoryPoint( vtx_pos, mom );
109  truth.Add( part );
110  }
111 }
112 
113 //------------------------------------------------------------------------------
115  const TLorentzVector& vtx_pos, marley::Event* marley_event )
116 {
117  simb::MCTruth truth;
118 
119  truth.SetOrigin( simb::kSuperNovaNeutrino );
120 
121  marley::Event event = fMarleyGenerator->create_event();
122 
123  // Add the initial and final state particles to the MCTruth object.
124  add_marley_particles( truth, event.get_initial_particles(), vtx_pos, false );
125  add_marley_particles( truth, event.get_final_particles(), vtx_pos, true );
126 
127  // calculate a few parameters for the call to SetNeutrino
128  const marley::Particle& nu = event.projectile();
129  const marley::Particle& lep = event.ejectile();
130  double qx = nu.px() - lep.px();
131  double qy = nu.py() - lep.py();
132  double qz = nu.pz() - lep.pz();
133  double Enu = nu.total_energy();
134  double Elep = lep.total_energy();
135  double Q2 = qx*qx + qy*qy + qz*qz - std::pow(Enu - Elep, 2);
136 
137  // For definitions of Bjorken x, etc., a good reference is Mark Thomson's
138  // set of slides on deep inelastic scattering (http://tinyurl.com/hcn5n6l)
139  double bjorken_x = Q2 / (2 * event.target().mass() * (Enu - Elep));
140  double inelasticity_y = 1. - Elep / Enu;
141 
142  // Include the initial excitation energy of the final-state nucleus when
143  // calculating W (the final-state invariant mass of the hadronic system)
144  // since the other parameters (X, Y) also take into account the 2-2
145  // scattering reaction only.
146  const marley::Particle& res = event.residue();
147  double hadronic_mass_W = res.mass() + event.Ex();
148 
149  // TODO: do a more careful job of setting the parameters here
150  truth.SetNeutrino(
151  simb::kCC, // change when MARLEY can handle NC
152  simb::kUnknownInteraction, // not sure what the mode should be
153  simb::kUnknownInteraction, // not sure what the interaction type should be
154  marley_utils::get_nucleus_pid(18, 40), // Ar-40 PDG code
155  marley_utils::NEUTRON, // nucleon PDG
156  0, // MARLEY handles low enough energies that we shouldn't need HitQuark
157  hadronic_mass_W * MeV_to_GeV,
158  bjorken_x, // dimensionless
159  inelasticity_y, // dimensionless
160  Q2 * std::pow(MeV_to_GeV, 2)
161  );
162 
163  if ( marley_event ) *marley_event = event;
164 
165  // Process the MARLEY logging messages (if any) captured by our
166  // stringstream and forward them to the messagefacility logger
167  std::string line;
168  while( std::getline(fMarleyLogStream, line) ) {
169  MF_LOG_INFO( fHelperName ) << line;
170  }
171 
172  // Reset the MARLEY log stream
173  fMarleyLogStream = std::stringstream();
174 
175  return truth;
176 }
177 
178 //------------------------------------------------------------------------------
179 std::string evgen::MARLEYHelper::find_file( const std::string& fileName,
180  const std::string& fileType )
181 {
182  cet::search_path searchPath( "FW_SEARCH_PATH" );
183 
184  std::string fullName;
185  searchPath.find_file( fileName, fullName );
186 
187  if ( fullName.empty() )
188  throw cet::exception( "MARLEYHelper" )
189  << "Cannot find MARLEY " << fileType << " data file '"
190  << fileName << '\'';
191 
192  return fullName;
193 }
194 
195 //------------------------------------------------------------------------------
197  marley::JSON& json, const std::string& key, bool missing_ok )
198 {
199  if ( json.has_key(key) ) {
200 
201  marley::JSON& value = json.at(key);
202 
203  if ( value.is_array() ) {
204  // Replace each file name (which may appear in the FHiCL configuration
205  // without a full path) with the full path found using cetlib
206  for ( auto& element : value.array_range() ) {
207  element = find_file( element.to_string(), key );
208  }
209  }
210 
211  else value = find_file(value.to_string(), key);
212  }
213  else if ( !missing_ok ) throw cet::exception("MARLEYHelper")
214  << "Missing \"" << key << "\" key in the MARLEY parameters.";
215 }
216 
217 //------------------------------------------------------------------------------
218 void evgen::MARLEYHelper::reconfigure( const fhicl::ParameterSet& pset )
219 {
220  // Convert the FHiCL parameters into a JSON object that MARLEY can understand
222  pset.walk( mpsw );
223 
224  marley::JSON& json = mpsw.get_json();
225 
226  // Update the reaction and structure data file names to the full paths
227  // using cetlib to search for them
228  load_full_paths_into_json( json, "reactions", false );
229  load_full_paths_into_json( json, "structure", true );
230 
231  // Also update the path for a neutrino source spectrum given in a ROOT
232  // TFile
233  if ( json.has_key("source") ) {
234  marley::JSON& source_object = json.at( "source" );
235 
236  if ( source_object.has_key("tfile") ) {
237  load_full_paths_into_json( source_object, "tfile" );
238  }
239  }
240 
241  // Create a new MARLEY configuration based on the JSON parameters
242  MF_LOG_INFO( "MARLEYHelper " + fHelperName ) << "MARLEY will now use"
243  " the JSON configuration\n" << json.dump_string() << '\n';
244  marley::RootJSONConfig config( json );
245 
246  // Create a new marley::Generator object based on the current configuration
247  fMarleyGenerator = std::make_unique<marley::Generator>(
248  config.create_generator() );
249 }
250 
251 //------------------------------------------------------------------------------
253 {
254  static bool already_loaded_marley_dict = false;
255 
256  if ( already_loaded_marley_dict ) return;
257 
258  // Current (24 July 2016) versions of ROOT 6 require runtime
259  // loading of headers for custom classes in order to use
260  // dictionaries correctly. If we're running ROOT 6+, do the
261  // loading here, and give the user guidance if there are any
262  // problems.
263  //
264  // This is the same technique used in the MARLEY source code
265  // for the executable (src/marley.cc). If you change how this
266  // code works, please sync changes with the executable as well.
267  if ( gROOT->GetVersionInt() >= 60000 ) {
268  MF_LOG_INFO( "MARLEYHelper " + fHelperName ) << "ROOT 6 or greater"
269  << " detected. Loading class information\nfrom headers"
270  << " \"marley/Particle.hh\" and \"marley/Event.hh\"";
271  TInterpreter::EErrorCode* ec = new TInterpreter::EErrorCode();
272  gInterpreter->ProcessLine( "#include \"marley/Particle.hh\"", ec );
273  if ( *ec != 0 ) throw cet::exception( "MARLEYHelper " + fHelperName )
274  << "Error loading MARLEY header Particle.hh. For MARLEY headers stored"
275  << " in /path/to/include/marley/, please add /path/to/include"
276  << " to your ROOT_INCLUDE_PATH environment variable and"
277  << " try again.";
278  gInterpreter->ProcessLine( "#include \"marley/Event.hh\"" );
279  if ( *ec != 0 ) throw cet::exception( "MARLEYHelper" ) << "Error loading"
280  << " MARLEY header Event.hh. For MARLEY headers stored in"
281  << " /path/to/include/marley/, please add /path/to/include"
282  << " to your ROOT_INCLUDE_PATH environment variable and"
283  << " try again.";
284  }
285 
286  // No further action is required for ROOT 5 because the compiled
287  // dictionaries (which are linked to this algorithm) contain all of
288  // the needed information
289  already_loaded_marley_dict = true;
290 }
LArSoft interface to the MARLEY (Model of Argon Reaction Low Energy Yields) supernova neutrino event ...
std::unique_ptr< marley::Generator > fMarleyGenerator
Definition: MARLEYHelper.h:73
var pdg
Definition: selectors.fcl:14
void add_marley_particles(simb::MCTruth &truth, const std::vector< marley::Particle * > &particles, const TLorentzVector &vtx_pos, bool track)
BEGIN_PROLOG pz
Definition: filemuons.fcl:10
MARLEYHelper(const fhicl::ParameterSet &pset, rndm::NuRandomService &rand_service, const std::string &generator_name)
simb::MCTruth create_MCTruth(const TLorentzVector &vtx_pos, marley::Event *marley_event=nullptr)
pdgs p
Definition: selectors.fcl:22
Concrete fhicl::ParameterSetWalker that converts a fhicl::ParameterSet into a marley::JSON object...
process_name E
process_name use argoneut_mc_hitfinder track
const marley::JSON & get_json() const
std::stringstream fMarleyLogStream
Definition: MARLEYHelper.h:80
std::string fHelperName
Definition: MARLEYHelper.h:76
std::string find_file(const std::string &fileName, const std::string &fileType)
Charged-current interactions.
Definition: IPrediction.h:38
void reconfigure(const fhicl::ParameterSet &pset)
unsigned int seed
BEGIN_PROLOG px
Definition: filemuons.fcl:10
void load_full_paths_into_json(marley::JSON &json, const std::string &array_name, bool missing_ok=false)
process_name physics producers generator physics producers generator physics producers generator py
float mass
Definition: dedx.py:47
do i e
temporary value
BEGIN_PROLOG SN nu
BEGIN_PROLOG don t mess with this pandoraTrackGausCryoW true