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