gparticle
Loading...
Searching...
No Matches
gparticle.cc
Go to the documentation of this file.
1// guts
2#include "gutilities.h"
3
4// gparticle
5#include "gparticle.h"
6#include "gparticle_options.h"
7
8// gemc
10
11// geant4
12#include "G4ParticleTable.hh"
13#include "Randomize.hh"
14
15using std::to_string;
16using std::ostream;
17using std::endl;
18using std::string;
19
20// Constructor based on parameters.
21// Detailed API documentation is in gparticle.h.
22Gparticle::Gparticle(const std::string& aname,
23 int amultiplicity,
24 double ap,
25 double adelta_p,
26 const std::string& punit,
27 const std::string& arandomMomentumModel,
28 double atheta,
29 double adelta_theta,
30 const std::string& arandomThetaModel,
31 double aphi,
32 double adelta_phi,
33 const std::string& aunit,
34 double avx,
35 double avy,
36 double avz,
37 double adelta_vx,
38 double adelta_vy,
39 double adelta_vz,
40 const std::string& vunit,
41 const std::string& arandomVertexModel,
42 const std::shared_ptr<GLogger>& logger,
43 int agenerator_type) :
44 name(aname),
45 generator_type(agenerator_type),
46 multiplicity(amultiplicity),
47 // Convert user values + unit strings into Geant4 numeric values through gutilities.
48 p(gutilities::getG4Number(to_string(ap) + "*" + punit)),
49 delta_p(gutilities::getG4Number(to_string(adelta_p) + "*" + punit)),
50 randomMomentumModel(gutilities::stringToRandomModel(arandomMomentumModel)),
51 theta(gutilities::getG4Number(to_string(atheta) + "*" + aunit)),
52 delta_theta(gutilities::getG4Number(to_string(adelta_theta) + "*" + aunit)),
53 randomThetaModel(gutilities::stringToRandomModel(arandomThetaModel)),
54 phi(gutilities::getG4Number(to_string(aphi) + "*" + aunit)),
55 delta_phi(gutilities::getG4Number(to_string(adelta_phi) + "*" + aunit)),
56 v(G4ThreeVector(
57 gutilities::getG4Number(to_string(avx) + "*" + vunit),
58 gutilities::getG4Number(to_string(avy) + "*" + vunit),
59 gutilities::getG4Number(to_string(avz) + "*" + vunit)
60 )),
61 delta_v(G4ThreeVector(
62 gutilities::getG4Number(to_string(adelta_vx) + "*" + vunit),
63 gutilities::getG4Number(to_string(adelta_vy) + "*" + vunit),
64 gutilities::getG4Number(to_string(adelta_vz) + "*" + vunit)
65 )),
66 randomVertexModel(gutilities::stringToRandomModel(arandomVertexModel)),
67 log(logger) {
68 // Resolve PDG id immediately so errors are detected early and configuration printing is complete.
69 pid = get_pdg_id();
70
71 log->debug(CONSTRUCTOR, "Gparticle");
72}
73
74
75// Shoots this particle into the provided event using the provided Geant4 particle gun.
76// Detailed API documentation is in gparticle.h.
77void Gparticle::shootParticle(G4ParticleGun* particleGun, G4Event* anEvent) {
78 auto particleTable = G4ParticleTable::GetParticleTable();
79 runtimeRecords.clear();
80
81 if (particleTable) {
82 // Resolve the particle definition by name.
83 auto particleDef = particleTable->FindParticle(name);
84
85 if (particleDef) {
86 // Mass is used to convert randomized momentum magnitude into kinetic energy.
87 double mass = particleDef->GetPDGMass();
88 particleGun->SetParticleDefinition(particleDef);
89
90
91 // Shoot one primary vertex per multiplicity.
92 for (int i = 0; i < multiplicity; i++) {
93 auto pmev = calculateMomentum();
94 auto kenergy = sqrt(pmev * pmev + mass * mass) - mass;
95 auto thetaRad = randomizeNumberFromSigmaWithModel(theta, delta_theta, randomThetaModel) / CLHEP::rad;
96 auto phiRad = randomizeNumberFromSigmaWithModel(phi, delta_phi, gutilities::uniform) / CLHEP::rad;
97 auto beamDirection = calculateBeamDirection(thetaRad, phiRad);
98 auto vertex = calculateVertex();
99
100 setRunTimeQuantities(kenergy, beamDirection, vertex);
101 runtimeRecords.push_back({
102 name,
103 pid,
104 generator_type,
105 pmev,
106 thetaRad * CLHEP::rad,
107 phiRad * CLHEP::rad,
108 vertex
109 });
110
111 particleGun->SetParticleEnergy(kenergy);
112 particleGun->SetParticleMomentumDirection(beamDirection);
113 particleGun->SetParticlePosition(vertex);
114 particleGun->GeneratePrimaryVertex(anEvent);
115
116 log->info(2, *this);
117 }
118 }
119 else {
121 "Particle <", name, "> not found in G4ParticleTable* ", particleTable);
122 }
123 }
124 else {
126 "G4ParticleTable not found - G4ParticleGun*: ", particleGun);
127 }
128}
129
130
131double Gparticle::calculateMomentum() {
132 // randomizeNumberFromSigmaWithModel applies the model-dependent interpretation of delta.
133 double pmev = randomizeNumberFromSigmaWithModel(p, delta_p, randomMomentumModel);
134
135 return pmev;
136}
137
138double Gparticle::calculateKinEnergy(double mass) {
139 double pmev = calculateMomentum();
140
141 return sqrt(pmev * pmev + mass * mass) - mass;
142}
143
144
145G4ThreeVector Gparticle::calculateBeamDirection(double thetaRad, double phiRad) {
146 G4ThreeVector pdir = G4ThreeVector(
147 cos(phiRad) * sin(thetaRad),
148 sin(phiRad) * sin(thetaRad),
149 cos(thetaRad)
150 );
151
152 return pdir;
153}
154
155G4ThreeVector Gparticle::calculateVertex() {
156 double x, y, z;
157
158 switch (randomVertexModel) {
160 // Component-wise uniform sampling around the nominal vertex.
161 x = randomizeNumberFromSigmaWithModel(v.x(), delta_v.x(), gutilities::uniform);
162 y = randomizeNumberFromSigmaWithModel(v.y(), delta_v.y(), gutilities::uniform);
163 z = randomizeNumberFromSigmaWithModel(v.z(), delta_v.z(), gutilities::uniform);
164 break;
165
167 // Component-wise Gaussian sampling around the nominal vertex (deltas used as sigmas).
168 x = randomizeNumberFromSigmaWithModel(v.x(), delta_v.x(), gutilities::gaussian);
169 y = randomizeNumberFromSigmaWithModel(v.y(), delta_v.y(), gutilities::gaussian);
170 z = randomizeNumberFromSigmaWithModel(v.z(), delta_v.z(), gutilities::gaussian);
171 break;
172
173 case gutilities::sphere: {
174 // Sample an offset inside a sphere-like region whose maximum radius is delta_v.r().
175 // Assumes all three components have comparable spread such that delta_v.r() is meaningful.
176 double radius;
177 double max_radius = delta_v.r();
178
179 // Rejection sampling: generate a random point in a cube until it lies within the radius bound.
180 do {
181 x = randomizeNumberFromSigmaWithModel(0, max_radius, gutilities::uniform);
182 y = randomizeNumberFromSigmaWithModel(0, max_radius, gutilities::uniform);
183 z = randomizeNumberFromSigmaWithModel(0, max_radius, gutilities::uniform);
184 radius = x * x + y * y + z * z;
185 }
186 while (radius > max_radius);
187
188 // Offset the sampled point by the nominal vertex.
189 x = x + v.x();
190 y = y + v.y();
191 z = z + v.z();
192 break;
193 }
194
195 default:
196 // Unknown model: fall back to deterministic vertex.
197 x = v.x();
198 y = v.y();
199 z = v.z();
200 break;
201 }
202
203 return {x, y, z};
204}
205
206
207double Gparticle::randomizeNumberFromSigmaWithModel(double center, double delta, gutilities::randomModel model) const {
208 switch (model) {
210 // Uniform in [center-delta, center+delta].
211 return center + ( 2.0 * G4UniformRand() - 1.0 ) * delta;
212
214 // Gaussian with mean=center and sigma=delta.
215 return G4RandGauss::shoot(center, delta);
216
217 case gutilities::cosine: {
218 // assuming this is an angle with corrected units
219 // For cosine-weighted sampling we work in radians, sample theta with sin(theta) weighting,
220 // and then enforce the requested [center-delta, center+delta] range.
221 double lower = ( center - delta ) / CLHEP::rad;
222 double upper = ( center + delta ) / CLHEP::rad;
223 double center_rad = 0;
224
225 if (lower < upper) {
226 // Generate theta such that cos(theta) is uniform, which corresponds to sin(theta) weighting.
227 do { center_rad = acos(1 - 2 * G4UniformRand()); }
228 while (center_rad < lower || center_rad > upper);
229 }
230 else {
231 // Degenerate range: fall back to the stored theta value.
232 center_rad = theta / CLHEP::rad;
233 }
234
235 return center_rad * CLHEP::rad;
236 }
237
238 default:
239 // Unknown model: no randomization.
240 return center;
241 }
242}
243
244// ---------------------------------------------------------------------------
245// pretty printer
246// ---------------------------------------------------------------------------
247std::ostream& operator<<(std::ostream& os, const Gparticle& gp) {
248 using std::left;
249 using std::right;
250 using std::setw;
251
252 constexpr int label_w = 15; // width for the field name (with ':')
253 constexpr int value_w = 12; // width for the main column
254
255 // helper: plain value
256 auto show = [&](const std::string& label, const auto& value) {
257 os << left << setw(label_w) << label << ' '
258 << setw(value_w) << right << setw(value_w) << value << '\n';
259 };
260
261 // helper: double value with N decimals
262 auto showf = [&](const std::string& label, double value, int prec = 3) {
263 std::streamsize old_prec = os.precision(); // save current
264 auto old_flags = os.flags(); // save flags
265
266 os << left << setw(label_w) << label << ' '
267 << right << setw(value_w) << std::fixed
268 << std::setprecision(prec) << value << '\n';
269
270 os.precision(old_prec); // restore
271 os.flags(old_flags);
272 };
273
274 // helper: value ± error (both doubles)
275 auto show_pm = [&](const std::string& label,
276 double val, double err,
277 int prec = 3) {
278 std::streamsize old_prec = os.precision();
279 auto old_flags = os.flags();
280
281 os << left << setw(label_w) << label << ' '
282 << right << setw(value_w) << std::fixed << setw(value_w)
283 << std::setprecision(prec) << val
284 << " ± " << std::setprecision(prec) << err << '\n';
285
286 os.precision(old_prec);
287 os.flags(old_flags);
288 };
289
290 // -----------------------------------------------------------------------
291 // header block
292 // -----------------------------------------------------------------------
293 os << '\n'
294 << " ┌─────────────────────────────────────────────────┐\n"
295 << " │ GParticle │\n"
296 << " └─────────────────────────────────────────────────┘\n";
297
298 // -----------------------------------------------------------------------
299 // fields
300 // -----------------------------------------------------------------------
301 os << left << setw(label_w) << " name:" << right << setw(value_w)
302 << gp.name << "(pid " << gp.pid << ")\n";
303
304 show(" multiplicity:", std::to_string(gp.multiplicity));
305 showf(" mass [MeV]:", gp.get_mass());
306
307 show_pm(" p [MeV]:",
308 gp.p / CLHEP::MeV,
309 gp.delta_p / CLHEP::MeV);
310
311 show(" p model:", to_string(gp.randomMomentumModel));
312
313 show_pm(" theta [deg]:",
314 gp.theta / CLHEP::deg,
315 gp.delta_theta / CLHEP::deg);
316
317 show(" theta model:", to_string(gp.randomThetaModel));
318
319 show_pm(" phi [deg]:",
320 gp.phi / CLHEP::deg,
321 gp.delta_phi / CLHEP::deg);
322
323 os << left << setw(label_w) << " vertex [cm]:" << ' '
324 << gp.v << " ± " << gp.delta_v << '\n';
325
326 show(" vertex model:", to_string(gp.randomVertexModel));
327
328 showf(" kinematic energy [MeV]:", gp.kinenergy / CLHEP::MeV);
329 os << left << setw(label_w) << " beam direction :" << ' '
330 << gp.beamDirection << '\n';
331
332 os << left << setw(label_w) << " vertex :" << ' '
333 << gp.vertex << '\n';
334
335
336 return os;
337}
338
339
340int Gparticle::get_pdg_id() {
341 auto particleTable = G4ParticleTable::GetParticleTable();
342
343 if (particleTable) {
344 auto particleDef = particleTable->FindParticle(name);
345
346 if (particleDef != nullptr) { return particleDef->GetPDGEncoding(); }
347 else {
349 "Particle <", name, "> not found in G4ParticleTable* ", particleTable);
350 }
351 }
352 else {
354 "G4ParticleTable not found - G4ParticleGun*: ", particleTable);
355 }
356}
357
358
359double Gparticle::get_mass() const {
360 auto particleTable = G4ParticleTable::GetParticleTable();
361
362 if (particleTable) {
363 auto particleDef = particleTable->FindParticle(name);
364
365 if (particleDef) {
366 double mass = particleDef->GetPDGMass();
367 return mass;
368 }
369 }
370 return 0;
371}
void debug(debug_type type, Args &&... args) const
void info(int level, Args &&... args) const
void error(int exit_code, Args &&... args) const
Lightweight particle specification and primary vertex shooter.
Definition gparticle.h:89
void shootParticle(G4ParticleGun *particleGun, G4Event *anEvent)
Shoots this particle configuration into a Geant4 event.
Definition gparticle.cc:77
Gparticle(const std::string &name, int multiplicity, double p, double delta_p, const std::string &punit, const std::string &randomMomentumModel, double theta, double delta_theta, const std::string &thetaModel, double phi, double delta_phi, const std::string &aunit, double avx, double avy, double avz, double adelta_vx, double adelta_vy, double adelta_vz, const std::string &vunit, const std::string &randomVertexModel, const std::shared_ptr< GLogger > &logger, int generator_type=1)
Constructs a particle configuration from user-facing parameters.
Definition gparticle.cc:22
CONSTRUCTOR
Conventions and error codes for the gparticle module.
#define ERR_GPARTICLETABLENOTFOUND
G4ParticleTable could not be obtained (unexpected runtime state).
#define ERR_GPARTICLENOTFOUND
Requested particle name was not found in the G4ParticleTable.
std::ostream & operator<<(std::ostream &os, const Gparticle &gp)
Definition gparticle.cc:247
Definition of the Gparticle class used by the gparticle module.
Public API for defining and parsing gparticle-related options.
double getG4Number(const string &v, bool warnIfNotUnit=false)
randomModel stringToRandomModel(const std::string &str)