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