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