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
193 // initializing...
194 xsec_total_ = 0;
195 ev_weighting_integral_.resize(targets_.size(), 0.0);
196 evg_drivers_.resize(targets_.size());
197
198 // calculate the total xsec per target...
199 for (size_t i_t = 0; i_t < targets_.size(); ++i_t) {
200 genie::InitialState initial_state(targets_[i_t], 11);
201 evg_drivers_[i_t].SetEventGeneratorList(
202 genie::RunOpt::Instance()->EventGeneratorList());
203 evg_drivers_[i_t].SetUnphysEventMask(
204 *genie::RunOpt::Instance()->UnphysEventMask());
205 evg_drivers_[i_t].Configure(initial_state);
206 evg_drivers_[i_t].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_drivers_[i_t].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 // setup the initial election
303 TParticle initial_e;
304 initial_e.SetPdgCode(11);
305 float elec_i_p =
306 std::sqrt(energy_ * energy_ - initial_e.GetMass() * initial_e.GetMass());
307 initial_e.SetMomentum(elec_i_p * direction_[0], elec_i_p * direction_[1],
308 elec_i_p * direction_[2], energy_);
309 TLorentzVector e_p4;
310 initial_e.Momentum(e_p4);
311
312 ldmx_log(debug) << "Generating interation with (px,py,pz,e)=" << "("
313 << e_p4.Px() << "," << e_p4.Py() << "," << e_p4.Pz() << ","
314 << e_p4.E() << ")";
315
316 n_events_by_target_[nucl_target_i] += 1;
317
318 // GENIE magic — use the pre-configured driver for this target
319 genie::EventRecord* genie_event = NULL;
320 while (!genie_event)
321 genie_event = evg_drivers_[nucl_target_i].GenerateEvent(e_p4);
322
323 auto ev_info = new UserEventInformation;
324 auto hepmc3_genie = hep_mc3_converter_.ConvertToHepMC3(*genie_event);
325 ldmx::HepMC3GenEvent hepmc3_ldmx_genie;
326 hepmc3_genie->write_data(hepmc3_ldmx_genie);
327 ev_info->addHepMC3GenEvent(hepmc3_ldmx_genie);
328 event->SetUserInformation(ev_info);
329
330 // setup the primary vertex now
331
332 G4PrimaryVertex* vertex = new G4PrimaryVertex();
333 vertex->SetPosition(x_pos, y_pos, z_pos);
334 vertex->SetWeight(genie_event->Weight());
335
336 // loop over the entries and add to the G4Event
337 int n_entries = genie_event->GetEntries();
338
339 ldmx_log(debug) << "---------- " << "Generated Event "
340 << n_events_generated_ + 1 << " ----------";
341
342 for (int i_p = 0; i_p < n_entries; ++i_p) {
343 genie::GHepParticle* p = (genie::GHepParticle*)(*genie_event)[i_p];
344
345 // make sure it's a final state particle
346 if (p->Status() != 1) continue;
347
348 ldmx_log(debug) << "\tAdding particle " << p->Pdg() << " with status "
349 << p->Status() << " energy " << p->E() << " ...";
350
351 G4PrimaryParticle* primary = new G4PrimaryParticle();
352 primary->SetPDGcode(p->Pdg());
353
354 // this sets the 4momentum. But may not respect masses in G4...
355 // primary->Set4Momentum(p->Px() * CLHEP::GeV, p->Py() * CLHEP::GeV,
356 // p->Pz() * CLHEP::GeV, p->E() * CLHEP::GeV);
357
358 // for now, do this to set the masses to be the G4 ones, through the PDG
359 // code
360 primary->SetMomentum(p->Px() * CLHEP::GeV, p->Py() * CLHEP::GeV,
361 p->Pz() * CLHEP::GeV);
362
363 primary->SetProperTime(time_ * CLHEP::ns);
364
365 UserPrimaryParticleInformation* primary_info =
367 primary_info->setHepEvtStatus(1);
368 primary->SetUserInformation(primary_info);
369
370 vertex->SetPrimary(primary);
371 }
372
373 // add the vertex to the event
374 event->AddPrimaryVertex(vertex);
375
376 // Apply beam spot smearing if configured for this generator
377 if (useBeamspot()) {
378 smearBeamspot(vertex);
379 }
380
381 ++n_events_generated_;
382 delete genie_event;
383}
384
385void GenieGenerator::RecordConfig(const std::string& id, ldmx::RunHeader& rh) {
386 rh.setStringParameter(id + " Class", "simcore::generators::GenieGenerator");
387 rh.setStringParameter(id + "GenieTune", tune_);
388}
389
390} // namespace generators
391} // namespace simcore
392
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.
bool useBeamspot() const
Check if beam spot smearing is enabled for this generator.
void smearBeamspot(G4PrimaryVertex *primary_vertex)
Apply beam spot smearing to a primary vertex.
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.
GenieGenerator(const std::string &name, const framework::config::Parameters &parameters)
Constructor.
std::vector< genie::GEVGDriver > evg_drivers_
One GENIE event generator driver per target and convertor to HepMC3GenEvent.
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 ...