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