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"
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(2, "Generated particle <", name, "> pid ", pid,
101 ", p [MeV]: ", pmev / CLHEP::MeV,
102 ", theta [deg]: ", thetaRad / CLHEP::deg,
103 ", phi [deg]: ", phiRad / CLHEP::deg,
104 ", vertex [cm]: ", vertex / CLHEP::cm);
105 }
106 }
107 else {
109 "Particle <", name, "> not found in G4ParticleTable* ", particleTable);
110 }
111 }
112 else {
114 "G4ParticleTable not found - G4ParticleGun*: ", particleGun);
115 }
116
117 return runtime_records;
118}
119
120
121double Gparticle::calculateMomentum() const {
122 // randomizeNumberFromSigmaWithModel applies the model-dependent interpretation of delta.
123 double pmev = randomizeNumberFromSigmaWithModel(p, delta_p, randomMomentumModel);
124
125 return pmev;
126}
127
128double Gparticle::calculateKinEnergy(double mass) const {
129 double pmev = calculateMomentum();
130
131 return sqrt(pmev * pmev + mass * mass) - mass;
132}
133
134
135G4ThreeVector Gparticle::calculateBeamDirection(double thetaRad, double phiRad) const {
136 G4ThreeVector pdir = G4ThreeVector(
137 cos(phiRad) * sin(thetaRad),
138 sin(phiRad) * sin(thetaRad),
139 cos(thetaRad)
140 );
141
142 return pdir;
143}
144
145G4ThreeVector Gparticle::calculateVertex() const {
146 double x, y, z;
147
148 switch (randomVertexModel) {
150 // Component-wise uniform sampling around the nominal vertex.
151 x = randomizeNumberFromSigmaWithModel(v.x(), delta_v.x(), gutilities::uniform);
152 y = randomizeNumberFromSigmaWithModel(v.y(), delta_v.y(), gutilities::uniform);
153 z = randomizeNumberFromSigmaWithModel(v.z(), delta_v.z(), gutilities::uniform);
154 break;
155
157 // Component-wise Gaussian sampling around the nominal vertex (deltas used as sigmas).
158 x = randomizeNumberFromSigmaWithModel(v.x(), delta_v.x(), gutilities::gaussian);
159 y = randomizeNumberFromSigmaWithModel(v.y(), delta_v.y(), gutilities::gaussian);
160 z = randomizeNumberFromSigmaWithModel(v.z(), delta_v.z(), gutilities::gaussian);
161 break;
162
163 case gutilities::sphere: {
164 // Sample an offset inside a sphere-like region whose maximum radius is delta_v.r().
165 // Assumes all three components have comparable spread such that delta_v.r() is meaningful.
166 double radius;
167 double max_radius = delta_v.r();
168
169 // Rejection sampling: generate a random point in a cube until it lies within the radius bound.
170 do {
171 x = randomizeNumberFromSigmaWithModel(0, max_radius, gutilities::uniform);
172 y = randomizeNumberFromSigmaWithModel(0, max_radius, gutilities::uniform);
173 z = randomizeNumberFromSigmaWithModel(0, max_radius, gutilities::uniform);
174 radius = x * x + y * y + z * z;
175 }
176 while (radius > max_radius * max_radius);
177
178 // Offset the sampled point by the nominal vertex.
179 x = x + v.x();
180 y = y + v.y();
181 z = z + v.z();
182 break;
183 }
184
185 default:
186 // Unknown model: fall back to deterministic vertex.
187 x = v.x();
188 y = v.y();
189 z = v.z();
190 break;
191 }
192
193 return {x, y, z};
194}
195
196
197double Gparticle::randomizeNumberFromSigmaWithModel(double center, double delta, gutilities::randomModel model) const {
198 switch (model) {
200 // Uniform in [center-delta, center+delta].
201 return center + ( 2.0 * G4UniformRand() - 1.0 ) * delta;
202
204 // Gaussian with mean=center and sigma=delta.
205 return G4RandGauss::shoot(center, delta);
206
207 case gutilities::cosine: {
208 // assuming this is an angle with corrected units
209 // For cosine-weighted sampling we work in radians, sample theta with sin(theta) weighting,
210 // and then enforce the requested [center-delta, center+delta] range.
211 double lower = ( center - delta ) / CLHEP::rad;
212 double upper = ( center + delta ) / CLHEP::rad;
213 double center_rad = 0;
214
215 if (lower < upper) {
216 // Generate theta such that cos(theta) is uniform, which corresponds to sin(theta) weighting.
217 do { center_rad = acos(1 - 2 * G4UniformRand()); }
218 while (center_rad < lower || center_rad > upper);
219 }
220 else {
221 // Degenerate range: fall back to the stored theta value.
222 center_rad = theta / CLHEP::rad;
223 }
224
225 return center_rad * CLHEP::rad;
226 }
227
228 default:
229 // Unknown model: no randomization.
230 return center;
231 }
232}
233
234// ---------------------------------------------------------------------------
235// pretty printer
236// ---------------------------------------------------------------------------
237std::ostream& operator<<(std::ostream& os, const Gparticle& gp) {
238 using std::left;
239 using std::right;
240 using std::setw;
241
242 constexpr int label_w = 15; // width for the field name (with ':')
243 constexpr int value_w = 12; // width for the main column
244
245 // helper: plain value
246 auto show = [&](const std::string& label, const auto& value) {
247 os << left << setw(label_w) << label << ' '
248 << setw(value_w) << right << setw(value_w) << value << '\n';
249 };
250
251 // helper: double value with N decimals
252 auto showf = [&](const std::string& label, double value, int prec = 3) {
253 std::streamsize old_prec = os.precision(); // save current
254 auto old_flags = os.flags(); // save flags
255
256 os << left << setw(label_w) << label << ' '
257 << right << setw(value_w) << std::fixed
258 << std::setprecision(prec) << value << '\n';
259
260 os.precision(old_prec); // restore
261 os.flags(old_flags);
262 };
263
264 // helper: value ± error (both doubles)
265 auto show_pm = [&](const std::string& label,
266 double val, double err,
267 int prec = 3) {
268 std::streamsize old_prec = os.precision();
269 auto old_flags = os.flags();
270
271 os << left << setw(label_w) << label << ' '
272 << right << setw(value_w) << std::fixed << setw(value_w)
273 << std::setprecision(prec) << val
274 << " ± " << std::setprecision(prec) << err << '\n';
275
276 os.precision(old_prec);
277 os.flags(old_flags);
278 };
279
280 // -----------------------------------------------------------------------
281 // header block
282 // -----------------------------------------------------------------------
283 os << '\n'
284 << " ┌─────────────────────────────────────────────────┐\n"
285 << " │ GParticle │\n"
286 << " └─────────────────────────────────────────────────┘\n";
287
288 // -----------------------------------------------------------------------
289 // fields
290 // -----------------------------------------------------------------------
291 os << left << setw(label_w) << " name:" << right << setw(value_w)
292 << gp.name << "(pid " << gp.pid << ")\n";
293
294 show(" multiplicity:", std::to_string(gp.multiplicity));
295 showf(" mass [MeV]:", gp.get_mass());
296
297 show_pm(" p [MeV]:",
298 gp.p / CLHEP::MeV,
299 gp.delta_p / CLHEP::MeV);
300
301 show(" p model:", to_string(gp.randomMomentumModel));
302
303 show_pm(" theta [deg]:",
304 gp.theta / CLHEP::deg,
305 gp.delta_theta / CLHEP::deg);
306
307 show(" theta model:", to_string(gp.randomThetaModel));
308
309 show_pm(" phi [deg]:",
310 gp.phi / CLHEP::deg,
311 gp.delta_phi / CLHEP::deg);
312
313 os << left << setw(label_w) << " vertex [cm]:" << ' '
314 << gp.v << " ± " << gp.delta_v << '\n';
315
316 show(" vertex model:", to_string(gp.randomVertexModel));
317
318 return os;
319}
320
321
322int Gparticle::get_pdg_id() {
323 auto particleTable = G4ParticleTable::GetParticleTable();
324
325 if (particleTable) {
326 auto particleDef = particleTable->FindParticle(name);
327
328 if (particleDef != nullptr) { return particleDef->GetPDGEncoding(); }
329 else {
331 "Particle <", name, "> not found in G4ParticleTable* ", particleTable);
332 }
333 }
334 else {
336 "G4ParticleTable not found - G4ParticleGun*: ", particleTable);
337 }
338}
339
340
341double Gparticle::get_mass() const {
342 auto particleTable = G4ParticleTable::GetParticleTable();
343
344 if (particleTable) {
345 auto particleDef = particleTable->FindParticle(name);
346
347 if (particleDef) {
348 double mass = particleDef->GetPDGMass();
349 return mass;
350 }
351 }
352 return 0;
353}
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:237
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