LDMX Software
GenieGenerator.cxx
Go to the documentation of this file.
1
8
10
11// GENIE
12#include "Framework/Conventions/Controls.h"
13#include "Framework/Conventions/GBuild.h"
14#include "Framework/Conventions/Units.h"
15#include "Framework/Conventions/XmlParserStatus.h"
16#include "Framework/EventGen/EventRecord.h"
17#include "Framework/EventGen/GEVGDriver.h"
18#include "Framework/EventGen/GFluxI.h"
19#include "Framework/EventGen/GMCJDriver.h"
20#include "Framework/EventGen/GMCJMonitor.h"
21#include "Framework/EventGen/InteractionList.h"
22#include "Framework/GHEP/GHepParticle.h"
23#include "Framework/Interaction/Interaction.h"
24#include "Framework/Messenger/Messenger.h"
25#include "Framework/Ntuple/NtpMCFormat.h"
26#include "Framework/Ntuple/NtpWriter.h"
27#include "Framework/Numerical/RandomGen.h"
28#include "Framework/Numerical/Spline.h"
29#include "Framework/ParticleData/PDGCodes.h"
30#include "Framework/ParticleData/PDGUtils.h"
31#include "Framework/Utils/AppInit.h"
32#include "Framework/Utils/CmdLnArgParser.h"
33#include "Framework/Utils/PrintUtils.h"
34#include "Framework/Utils/RunOpt.h"
35#include "Framework/Utils/StringUtils.h"
36#include "Framework/Utils/SystemUtils.h"
37#include "Framework/Utils/XSecSplineList.h"
38#include "GENIE/Framework/Interaction/InitialState.h"
39#include "GENIE/Framework/Utils/RunOpt.h"
40#include "HepMC3/GenEvent.h"
41
42// Geant4
43#include "G4Event.hh"
44#include "G4PhysicalConstants.hh"
45#include "Randomize.hh"
46
47// ROOT
48#include <Math/Vector3D.h>
49#include <TLorentzVector.h>
50#include <TParticle.h>
51
52#include "Math/Interpolator.h"
53
54// standard
55#include <algorithm>
56#include <iostream>
57#include <sstream>
58
59namespace simcore {
60namespace generators {
61
62void GenieGenerator::fillConfig(const framework::config::Parameters& p) {
63 energy_ = p.get<double>("energy"); // * GeV;
64
65 targets_ = p.get<std::vector<int> >("targets");
66 abundances_ = p.get<std::vector<double> >("abundances");
67
68 time_ = p.get<double>("time"); // * ns;
69 position_ = p.get<std::vector<double> >("position"); // mm
70 beam_size_ = p.get<std::vector<double> >("beam_size"); // mm
71 direction_ = p.get<std::vector<double> >("direction");
72 target_thickness_ = p.get<double>("target_thickness"); // mm
73
74 tune_ = p.get<std::string>("tune");
75 spline_file_ = p.get<std::string>("spline_file");
76
77 message_threshold_file_ = p.get<std::string>("message_threshold_file");
78}
79
81 bool ret = true;
82
83 if (targets_.size() == 0 || abundances_.size() == 0) {
84 ldmx_log(error) << "targets and/or abundances sizes are zero." << " "
85 << targets_.size() << ", " << abundances_.size();
86 ret = false;
87 }
88 if (targets_.size() != abundances_.size()) {
89 ldmx_log(error) << "targets and abundances sizes unequal." << " "
90 << targets_.size() << " != " << abundances_.size();
91 ret = false;
92 }
93
94 if (position_.size() != 3 || direction_.size() != 3) {
95 ldmx_log(error) << "position and/or direction sizes are not 3." << " "
96 << position_.size() << ", " << direction_.size();
97 ret = false;
98 }
99
100 if (target_thickness_ < 0) {
101 ldmx_log(warn) << "target thickness cannot be less than 0! (thickness="
102 << target_thickness_ << "). Taking absolute value.";
103 target_thickness_ = std::abs(target_thickness_);
104 }
105
106 if (beam_size_.size() != 2) {
107 if (beam_size_.size() == 0) {
108 ldmx_log(info) << "beam size not set. Using zero.";
109 beam_size_.resize(2);
110 beam_size_[0] = 0.0;
111 beam_size_[1] = 0.0;
112 } else {
113 ldmx_log(error) << "beam size is set, but does not have size 2." << " "
114 << beam_size_.size();
115 ret = false;
116 }
117 } else if (beam_size_[0] < 0 || beam_size_[1] < 0) {
118 ldmx_log(warn) << "Beam size set as negative value? " << "("
119 << beam_size_[0] << "," << beam_size_[1] << ")"
120 << ". Changing to positive.";
121 beam_size_[0] = std::abs(beam_size_[0]);
122 beam_size_[1] = std::abs(beam_size_[1]);
123 }
124
125 // normalize abundances
126 float abundance_sum = 0;
127 for (auto a : abundances_) {
128 abundance_sum += a;
129 }
130
131 if (std::abs(abundance_sum) < 1e-6) {
132 ldmx_log(error) << "abundances list sums to zero? " << abundance_sum;
133 ret = false;
134 }
135
136 if (std::abs(abundance_sum - 1.0) > 2e-2) {
137 ldmx_log(info) << "abundances list sums is not unity (" << abundance_sum
138 << " instead.) Will renormalize abundances to unity!";
139 }
140
141 for (size_t i_a = 0; i_a < abundances_.size(); ++i_a) {
142 abundances_[i_a] = abundances_[i_a] / abundance_sum;
143
144 ldmx_log(debug) << "Target=" << targets_[i_a]
145 << ", Abundance=" << abundances_[i_a];
146 }
147
148 float dir_total_sq = 0;
149 for (auto d : direction_) dir_total_sq += d * d;
150
151 if (dir_total_sq < 1e-6) {
152 ldmx_log(error) << "direction vector is zero or negative? " << "("
153 << direction_[0] << "," << direction_[1] << ","
154 << direction_[2] << ")";
155 ret = false;
156 }
157 for (size_t i_d = 0; i_d < direction_.size(); ++i_d)
158 direction_[i_d] = direction_[i_d] / std::sqrt(dir_total_sq);
159
160 xsec_by_target_.resize(targets_.size(), -999.);
161 n_events_by_target_.resize(targets_.size(), 0);
162
163 return ret;
164}
165
167 // initialize some RunOpt by hacking the command line interface
168 {
169 char* in_arr[3] = {const_cast<char*>(""),
170 const_cast<char*>("--event-generator-list"),
171 const_cast<char*>("EM")};
172 genie::RunOpt::Instance()->ReadFromCommandLine(3, in_arr);
173 }
174
175 // set message thresholds
176 genie::utils::app_init::MesgThresholds(message_threshold_file_);
177
178 // set tune info
179 genie::RunOpt::Instance()->SetTuneName(tune_);
180 if (!genie::RunOpt::Instance()->Tune()) {
181 EXCEPTION_RAISE("ConfigurationException", "No TuneId in RunOption.");
182 }
183 genie::RunOpt::Instance()->BuildTune();
184
185 // give it the splint file and require it
186 genie::utils::app_init::XSecTable(spline_file_, true);
187
188 // set GHEP print level (needed?)
189 genie::GHepRecord::SetPrintLevel(0);
190
191 // setup for event driver
192 evg_driver_.SetEventGeneratorList(
193 genie::RunOpt::Instance()->EventGeneratorList());
194 evg_driver_.SetUnphysEventMask(*genie::RunOpt::Instance()->UnphysEventMask());
195}
196
198 // initializing...
199 xsec_total_ = 0;
200 ev_weighting_integral_.resize(targets_.size(), 0.0);
201
202 // calculate the total xsec per target...
203 for (size_t i_t = 0; i_t < targets_.size(); ++i_t) {
204 genie::InitialState initial_state(targets_[i_t], 11);
205 evg_driver_.Configure(initial_state);
206 evg_driver_.UseSplines();
207
208 // setup the initial election
209 TParticle initial_e;
210 initial_e.SetPdgCode(11);
211 auto elec_i_p = std::sqrt(energy_ * energy_ -
212 initial_e.GetMass() * initial_e.GetMass());
213 initial_e.SetMomentum(elec_i_p * direction_[0], elec_i_p * direction_[1],
214 elec_i_p * direction_[2], energy_);
215 TLorentzVector e_p4;
216 initial_e.Momentum(e_p4);
217
218 xsec_by_target_[i_t] = evg_driver_.XSecSum(e_p4);
219 xsec_total_ += xsec_by_target_[i_t] * abundances_[i_t];
220
221 ev_weighting_integral_[i_t] = xsec_total_; // running sum
222
223 // print...
224 ldmx_log(debug) << "Target=" << targets_[i_t]
225 << "\tAbundance=" << abundances_[i_t] << "\tXSEC="
226 << xsec_by_target_[i_t] / genie::units::millibarn << "mb";
227 }
228 ldmx_log(debug) << "Total XSEC = " << xsec_total_ / genie::units::millibarn
229 << " mb";
230
231 // renormalize our weighting integral
232 for (size_t i_t = 0; i_t < ev_weighting_integral_.size(); ++i_t)
233 ev_weighting_integral_[i_t] = ev_weighting_integral_[i_t] / xsec_total_;
234}
235
236GenieGenerator::GenieGenerator(const std::string& name,
238 : PrimaryGenerator(name, p) {
239 fillConfig(p);
240
241 if (!validateConfig())
242 EXCEPTION_RAISE("ConfigurationException", "Configuration not valid.");
243
244 n_events_generated_ = 0;
247}
248
250 std::cout << "--- GENIE Generation Summary BEGIN ---" << std::endl;
251 double total_xsec = 0;
252 for (size_t i_t = 0; i_t < targets_.size(); ++i_t) {
253 std::cout << "Target=" << targets_[i_t]
254 << "\tAbundance=" << abundances_[i_t]
255 << "\tXSEC=" << xsec_by_target_[i_t] / genie::units::millibarn
256 << " mb" << "\tEvents=" << n_events_by_target_[i_t] << std::endl;
257 if (n_events_by_target_[i_t] > 0)
258 total_xsec += xsec_by_target_[i_t] * abundances_[i_t];
259 }
260
261 std::cout << "Total events generated = " << n_events_generated_
262 << "\nTotal XSEC = " << total_xsec / genie::units::millibarn
263 << " mb" << std::endl;
264
265 std::cout << "--- GENIE Generation Summary *END* ---" << std::endl;
266}
267
269 if (n_events_generated_ == 0) {
270 // set random seed
271 // have to do this here since seeds aren't properly set until we know the
272 // run number
273 auto seed = G4Random::getTheEngine()->getSeed();
274 ldmx_log(debug) << "Initializing GENIE with seed " << seed;
275 genie::utils::app_init::RandGen(seed);
276 }
277
278 auto nucl_target_i = 0;
279
280 if (targets_.size() > 0) {
281 double rand_uniform = G4Random::getTheGenerator()->flat();
282
283 nucl_target_i = std::distance(
284 ev_weighting_integral_.begin(),
285 std::lower_bound(ev_weighting_integral_.begin(),
286 ev_weighting_integral_.end(), rand_uniform));
287
288 ldmx_log(debug) << "Random number = " << rand_uniform << ", target picked "
289 << targets_.at(nucl_target_i);
290 }
291
292 auto x_pos = position_[0] +
293 (G4Random::getTheGenerator()->flat() - 0.5) * beam_size_[0];
294 auto y_pos = position_[1] +
295 (G4Random::getTheGenerator()->flat() - 0.5) * beam_size_[1];
296 auto z_pos = position_[2] +
297 (G4Random::getTheGenerator()->flat() - 0.5) * target_thickness_;
298
299 ldmx_log(debug) << "Generating interaction at (x_,y_,z_)=" << "(" << x_pos
300 << "," << y_pos << "," << z_pos << ")";
301
302 genie::InitialState initial_state(targets_.at(nucl_target_i), 11);
303 evg_driver_.Configure(initial_state);
304 evg_driver_.UseSplines();
305
306 // setup the initial election
307 TParticle initial_e;
308 initial_e.SetPdgCode(11);
309 float elec_i_p =
310 std::sqrt(energy_ * energy_ - initial_e.GetMass() * initial_e.GetMass());
311 initial_e.SetMomentum(elec_i_p * direction_[0], elec_i_p * direction_[1],
312 elec_i_p * direction_[2], energy_);
313 TLorentzVector e_p4;
314 initial_e.Momentum(e_p4);
315
316 ldmx_log(debug) << "Generating interation with (px,py,pz,e)=" << "("
317 << e_p4.Px() << "," << e_p4.Py() << "," << e_p4.Pz() << ","
318 << e_p4.E() << ")";
319
320 // calculate total xsec
321 if (n_events_by_target_[nucl_target_i] == 0)
322 xsec_by_target_[nucl_target_i] = evg_driver_.XSecSum(e_p4);
323
324 n_events_by_target_[nucl_target_i] += 1;
325
326 // GENIE magic
327 genie::EventRecord* genie_event = NULL;
328 while (!genie_event) genie_event = evg_driver_.GenerateEvent(e_p4);
329
330 auto ev_info = new UserEventInformation;
331 auto hepmc3_genie = hep_mc3_converter_.ConvertToHepMC3(*genie_event);
332 ldmx::HepMC3GenEvent hepmc3_ldmx_genie;
333 hepmc3_genie->write_data(hepmc3_ldmx_genie);
334 ev_info->addHepMC3GenEvent(hepmc3_ldmx_genie);
335 event->SetUserInformation(ev_info);
336
337 // setup the primary vertex now
338
339 G4PrimaryVertex* vertex = new G4PrimaryVertex();
340 vertex->SetPosition(x_pos, y_pos, z_pos);
341 vertex->SetWeight(genie_event->Weight());
342
343 // loop over the entries and add to the G4Event
344 int n_entries = genie_event->GetEntries();
345
346 ldmx_log(debug) << "---------- " << "Generated Event "
347 << n_events_generated_ + 1 << " ----------";
348
349 for (int i_p = 0; i_p < n_entries; ++i_p) {
350 genie::GHepParticle* p = (genie::GHepParticle*)(*genie_event)[i_p];
351
352 // make sure it's a final state particle
353 if (p->Status() != 1) continue;
354
355 ldmx_log(debug) << "\tAdding particle " << p->Pdg() << " with status "
356 << p->Status() << " energy " << p->E() << " ...";
357
358 G4PrimaryParticle* primary = new G4PrimaryParticle();
359 primary->SetPDGcode(p->Pdg());
360
361 // this sets the 4momentum. But may not respect masses in G4...
362 // primary->Set4Momentum(p->Px() * CLHEP::GeV, p->Py() * CLHEP::GeV,
363 // p->Pz() * CLHEP::GeV, p->E() * CLHEP::GeV);
364
365 // for now, do this to set the masses to be the G4 ones, through the PDG
366 // code
367 primary->SetMomentum(p->Px() * CLHEP::GeV, p->Py() * CLHEP::GeV,
368 p->Pz() * CLHEP::GeV);
369
370 primary->SetProperTime(time_ * CLHEP::ns);
371
372 UserPrimaryParticleInformation* primary_info =
374 primary_info->setHepEvtStatus(1);
375 primary->SetUserInformation(primary_info);
376
377 vertex->SetPrimary(primary);
378 }
379
380 // add the vertex to the event
381 event->AddPrimaryVertex(vertex);
382
383 ++n_events_generated_;
384 // delete genie_event;
385}
386
387void GenieGenerator::RecordConfig(const std::string& id, ldmx::RunHeader& rh) {
388 rh.setStringParameter(id + " Class", "simcore::generators::GenieGenerator");
389 rh.setStringParameter(id + "GenieTune", tune_);
390}
391
392} // namespace generators
393} // namespace simcore
394
Simple GENIE event generator.
#define DECLARE_GENERATOR(CLASS)
@macro DECLARE_GENERATOR
Class that provides extra information for Geant4 primary particles.
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:29
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:78
Run-specific configuration and data stored in its own output TTree alongside the event TTree in the o...
Definition RunHeader.h:57
void setStringParameter(const std::string &name, std::string value)
Set a string parameter value.
Definition RunHeader.h:222
Interface that defines a simulation primary generator.
Encapsulates user defined information associated with a Geant4 event.
Defines extra information attached to a Geant4 primary particle.
void setHepEvtStatus(int hepEvtStatus)
Set the HEP event status (generator status) e.g.
Class that uses GENIE's GEVGDriver to generator eN interactions.
void calculateTotalXS()
GENIE initialization.
void initializeGENIE()
simple validation check on configuration params
void RecordConfig(const std::string &id, ldmx::RunHeader &rh) final override
Record the configuration of the primary generator into the run header.
genie::GEVGDriver evg_driver_
The GENIE event generator driver and convertor to HepMC3GenEvent.
GenieGenerator(const std::string &name, const framework::config::Parameters &parameters)
Constructor.
bool validateConfig()
fill the configuration
void GeneratePrimaryVertex(G4Event *event) final override
Generate the primary vertices in the Geant4 event.
Dynamically loadable photonuclear models either from SimCore or external libraries implementing this ...