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