Garfield++ 5.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
TrackElectron.cc
Go to the documentation of this file.
1#include <cmath>
2#include <iostream>
3#include <numeric>
4
7#include "Garfield/Random.hh"
8#include "Garfield/Sensor.hh"
10
11namespace Garfield {
12
14
15 // Setup the particle properties.
16 m_q = -1;
17 m_spin = 1;
18 m_mass = ElectronMass;
19 m_isElectron = true;
20 SetBetaGamma(3.);
21 m_particleName = "electron";
22}
23
24void TrackElectron::SetParticle(const std::string& particle) {
25 if (particle != "electron" && particle != "e" && particle != "e-") {
26 std::cerr << m_className << "::SetParticle: Only electrons are allowed.\n";
27 }
28}
29
30bool TrackElectron::NewTrack(const double x0, const double y0, const double z0,
31 const double t0, const double dx0,
32 const double dy0, const double dz0) {
33 // Reset the list of clusters.
34 m_clusters.clear();
35 m_cluster = 0;
36 // Make sure the sensor has been set.
37 if (!m_sensor) {
38 std::cerr << m_className << "::NewTrack: Sensor is not defined.\n";
39 return false;
40 }
41
42 // Make sure the medium at this location is an ionisable gas.
43 Medium* medium = m_sensor->GetMedium(x0, y0, z0);
44 if (!medium || !medium->IsIonisable() || !medium->IsGas()) {
45 std::cerr << m_className << "::NewTrack:\n"
46 << " No ionisable gas medium at initial position.\n";
47 return false;
48 }
49 std::vector<Parameters> par;
50 std::vector<double> frac;
51 if (!Setup(medium, par, frac)) {
52 std::cerr << m_className << "::NewTrack:\n"
53 << " Properties of " << medium->GetName()
54 << " are not implemented.\n";
55 return false;
56 }
57
58 const std::string mediumName = medium->GetName();
59 const double density = medium->GetNumberDensity();
60
61 const size_t nComponents = frac.size();
62 std::vector<double> prob(nComponents, 0.);
63 double mfp = 0., dedx = 0.;
64 if (!Update(density, m_beta2, par, frac, prob, mfp, dedx)) {
65 std::cerr << m_className << "::NewTrack:\n"
66 << " Cross-sections could not be calculated.\n";
67 return false;
68 }
69 m_mfp = mfp;
70 m_dedx = dedx;
71
72 double x = x0;
73 double y = y0;
74 double z = z0;
75 double t = t0;
76 double dx = dx0;
77 double dy = dy0;
78 double dz = dz0;
79 const double d = sqrt(dx * dx + dy * dy + dz * dz);
80 if (d < Small) {
81 RndmDirection(dx, dy, dz);
82 } else {
83 // Normalize the direction vector.
84 const double scale = 1. / d;
85 dx *= scale;
86 dy *= scale;
87 dz *= scale;
88 }
89 const double dt = 1. / (sqrt(m_beta2) * SpeedOfLight);
90 double e0 = ElectronMass * (sqrt(1. / (1. - m_beta2)) - 1.);
91 while (e0 > 0.) {
92 // Draw a step length and propagate the electron.
93 const double step = -m_mfp * log(RndmUniformPos());
94 x += step * dx;
95 y += step * dy;
96 z += step * dz;
97 t += step * dt;
98
99 medium = m_sensor->GetMedium(x, y, z);
100 if (!medium || !medium->IsIonisable() ||
101 medium->GetName() != mediumName ||
102 medium->GetNumberDensity() != density) {
103 break;
104 }
105 Cluster cluster;
106 cluster.x = x;
107 cluster.y = y;
108 cluster.z = z;
109 cluster.t = t;
110 const double r = RndmUniform();
111 for (size_t i = 0; i < nComponents; ++i) {
112 if (r > prob[i]) continue;
113 // Sample secondary electron energy according to
114 // Opal-Beaty-Peterson splitting function.
115 cluster.esec = Esec(e0, par[i]);
116 m_clusters.push_back(std::move(cluster));
117 break;
118 }
119 }
120 m_cluster = m_clusters.size() + 2;
121 return true;
122}
123
124bool TrackElectron::GetCluster(double& xc, double& yc, double& zc, double& tc,
125 int& ne, double& ec, double& extra) {
126 xc = yc = zc = tc = ec = extra = 0.;
127 ne = 0;
128 if (m_clusters.empty()) return false;
129 // Increment the cluster index.
130 if (m_cluster < m_clusters.size()) {
131 ++m_cluster;
132 } else if (m_cluster > m_clusters.size()) {
133 m_cluster = 0;
134 }
135 if (m_cluster >= m_clusters.size()) return false;
136
137 xc = m_clusters[m_cluster].x;
138 yc = m_clusters[m_cluster].y;
139 zc = m_clusters[m_cluster].z;
140 tc = m_clusters[m_cluster].t;
141 ec = m_clusters[m_cluster].esec;
142 ne = 1;
143 return true;
144}
145
147 return m_mfp > 0. ? 1. / m_mfp : 0.;
148}
149
151 return m_dedx;
152}
153
154bool TrackElectron::Setup(Medium* gas, std::vector<Parameters>& par,
155 std::vector<double>& frac) {
156
157 if (!gas) {
158 std::cerr << "TrackElectron::Setup: Medium is not defined.\n";
159 return false;
160 }
161
162 const size_t nComponents = gas->GetNumberOfComponents();
163 if (nComponents == 0) {
164 std::cerr << "TrackElectron::Setup: Composition is not defined.\n";
165 return false;
166 }
167 par.resize(nComponents);
168 frac.assign(nComponents, 0.);
169
170 // Density correction parameters from
171 // R. M. Sternheimer, M. J. Berger, S. M. Seltzer,
172 // Atomic Data and Nuclear Data Tables 30 (1984), 261-271
173 for (size_t i = 0; i < nComponents; ++i) {
174 std::string gasname = "";
175 gas->GetComponent(i, gasname, frac[i]);
176 if (gasname == "CF4") {
177 par[i].m2 = 7.2;
178 par[i].cIon = 93.;
179 par[i].x0 = 1.;
180 par[i].x1 = 0.;
181 par[i].cDens = 0.;
182 par[i].aDens = 0.;
183 par[i].mDens = 0.;
184 par[i].ethr = 15.9;
185 par[i].wSplit = 19.5;
186 } else if (gasname == "Ar") {
187 par[i].m2 = 3.593;
188 par[i].cIon = 39.7;
189 par[i].x0 = 1.7635;
190 par[i].x1 = 4.4855;
191 par[i].cDens = 11.9480;
192 par[i].aDens = 0.19714;
193 par[i].mDens = 2.9618;
194 par[i].ethr = 15.75961;
195 par[i].wSplit = 15.;
196 } else if (gasname == "He") {
197 par[i].m2 = 0.489;
198 par[i].cIon = 5.5;
199 par[i].x0 = 2.2017;
200 par[i].x1 = 3.6122;
201 par[i].cDens = 11.1393;
202 par[i].aDens = 0.13443;
203 par[i].mDens = 5.8347;
204 par[i].ethr = 24.58739;
205 par[i].wSplit = 10.5;
206 } else if (gasname == "He-3") {
207 par[i].m2 = 0.489;
208 par[i].cIon = 5.5;
209 par[i].x0 = 2.2017;
210 par[i].x1 = 3.6122;
211 par[i].cDens = 11.1393;
212 par[i].aDens = 0.13443;
213 par[i].mDens = 5.8347;
214 par[i].ethr = 24.58739;
215 par[i].wSplit = 10.5;
216 } else if (gasname == "Ne") {
217 par[i].m2 = 1.69;
218 par[i].cIon = 17.8;
219 par[i].x0 = 2.0735;
220 par[i].x1 = 4.6421;
221 par[i].cDens = 11.9041;
222 par[i].aDens = 0.08064;
223 par[i].mDens = 3.5771;
224 par[i].ethr = 21.56454;
225 par[i].wSplit = 19.5;
226 } else if (gasname == "Kr") {
227 par[i].m2 = 5.5;
228 par[i].cIon = 56.9;
229 par[i].x0 = 1.7158;
230 par[i].x1 = 5.0748;
231 par[i].cDens = 12.5115;
232 par[i].aDens = 0.07446;
233 par[i].mDens = 3.4051;
234 par[i].ethr = 13.996;
235 par[i].wSplit = 21.;
236 } else if (gasname == "Xe") {
237 par[i].m2 = 8.04;
238 par[i].cIon = 75.25;
239 par[i].x0 = 1.5630;
240 par[i].x1 = 4.7371;
241 par[i].cDens = 12.7281;
242 par[i].aDens = 0.23314;
243 par[i].mDens = 2.7414;
244 par[i].ethr = 12.129843;
245 par[i].wSplit = 23.7;
246 } else if (gasname == "CH4") {
247 par[i].m2 = 3.75;
248 par[i].cIon = 42.5;
249 par[i].x0 = 1.6263;
250 par[i].x1 = 3.9716;
251 par[i].cDens = 9.5243;
252 par[i].aDens = 0.09253;
253 par[i].mDens = 3.6257;
254 par[i].ethr = 12.65;
255 par[i].wSplit = 8.;
256 } else if (gasname == "iC4H10") {
257 par[i].m2 = 15.5;
258 par[i].cIon = 160.;
259 par[i].x0 = 1.3788;
260 par[i].x1 = 3.7524;
261 par[i].cDens = 8.5633;
262 par[i].aDens = 0.10852;
263 par[i].mDens = 3.4884;
264 par[i].ethr = 10.67;
265 par[i].wSplit = 7.;
266 } else if (gasname == "CO2") {
267 par[i].m2 = 5.6;
268 par[i].cIon = 57.91;
269 par[i].x0 = 1.6294;
270 par[i].x1 = 4.1825;
271 par[i].aDens = 0.11768;
272 par[i].mDens = 3.3227;
273 par[i].ethr = 13.777;
274 par[i].wSplit = 13.;
275 } else if (gasname == "N2") {
276 par[i].m2 = 3.35;
277 par[i].cIon = 38.1;
278 par[i].x0 = 1.7378;
279 par[i].x1 = 4.1323;
280 par[i].cDens = 10.5400;
281 par[i].aDens = 0.15349;
282 par[i].mDens = 3.2125;
283 par[i].ethr = 15.581;
284 par[i].wSplit = 13.8;
285 } else {
286 std::cerr << "TrackElectron::Setup: Parameters for "
287 << gasname << " are not implemented.\n";
288 return false;
289 }
290 }
291 return true;
292}
293
294bool TrackElectron::Update(const double density, const double beta2,
295 const std::vector<Parameters>& par,
296 const std::vector<double>& frac,
297 std::vector<double>& prob,
298 double& mfp, double& dedx) {
299
300 if (beta2 <= 0.) return false;
301 const double lnBg2 = log(beta2 / (1. - beta2));
302 // Primary energy
303 const double e0 = ElectronMass * (sqrt(1. / (1. - beta2)) - 1.);
304 // Parameter X in the Sternheimer fit formula
305 const double eta = density / LoschmidtNumber;
306 const double x = 0.5 * (lnBg2 + log(eta)) / log(10.);
307
308 const size_t n = par.size();
309 prob.assign(n, 0.);
310 dedx = 0.;
311 for (size_t i = 0; i < n; ++i) {
312 const double delta = Delta(x, par[i]);
313 prob[i] = frac[i] * (par[i].m2 * (lnBg2 - beta2 - delta) + par[i].cIon);
314 // Calculate the mean secondary electron energy.
315 const double ew = (e0 - par[i].ethr) / (2 * par[i].wSplit);
316 const double emean = (par[i].wSplit / (2 * atan(ew))) * log1p(ew * ew);
317 dedx += prob[i] * emean;
318 }
319 // Normalise and add up the probabilities.
320 const double psum = std::accumulate(prob.begin(), prob.end(), 0.);
321 if (psum <= 0.) {
322 std::cerr << "TrackElectron::Update: Total cross-section <= 0.";
323 return false;
324 }
325 const double scale = 1. / psum;
326 for (size_t i = 0; i < n; ++i) {
327 prob[i] *= scale;
328 if (i > 0) prob[i] += prob[i - 1];
329 }
330 // Compute mean free path and stopping power.
331 constexpr double prefactor =
332 4 * Pi * HbarC * HbarC / (ElectronMass * ElectronMass);
333 const double cs = prefactor * psum / beta2;
334 mfp = 1. / (cs * density);
335 dedx *= density * prefactor / beta2;
336 return true;
337}
338
339double TrackElectron::Delta(const double x, const Parameters& par) {
340
341 double delta = 0.;
342 if (par.x0 < par.x1 && x >= par.x0) {
343 delta = 2 * log(10.) * x - par.cDens;
344 if (x < par.x1) {
345 delta += par.aDens * pow(par.x1 - x, par.mDens);
346 }
347 }
348 return delta;
349}
350
351double TrackElectron::Esec(const double e0, const Parameters& par) {
352 double esec = par.wSplit * tan(RndmUniform() * atan((e0 - par.ethr) /
353 (2. * par.wSplit)));
354 return par.wSplit * pow(esec / par.wSplit, 0.9524);
355}
356}
Abstract base class for media.
Definition Medium.hh:16
virtual void GetComponent(const unsigned int i, std::string &label, double &f)
Get the name and fraction of a given component.
Definition Medium.cc:106
virtual double GetNumberDensity() const
Get the number density [cm-3].
Definition Medium.hh:63
unsigned int GetNumberOfComponents() const
Get number of components of the medium.
Definition Medium.hh:48
const std::string & GetName() const
Get the medium name/identifier.
Definition Medium.hh:26
virtual bool IsGas() const
Is this medium a gas?
Definition Medium.hh:28
bool IsIonisable() const
Is charge deposition by charged particles/photon enabled in this medium?
Definition Medium.hh:81
void SetParticle(const std::string &particle) override
bool GetCluster(double &xc, double &yc, double &zc, double &tc, int &nc, double &ec, double &extra) override
bool NewTrack(const double x0, const double y0, const double z0, const double t0, const double dx0, const double dy0, const double dz0) override
double GetClusterDensity() override
double GetStoppingPower() override
Get the stopping power (mean energy loss [eV] per cm).
TrackElectron()
Constructor.
Sensor * m_sensor
Definition Track.hh:114
void SetBetaGamma(const double bg)
Set the relative momentum of the particle.
Definition Track.cc:106
bool m_isElectron
Definition Track.hh:111
std::string m_particleName
Definition Track.hh:112
double m_beta2
Definition Track.hh:110
std::string m_className
Definition Track.hh:104
double m_mass
Definition Track.hh:108
Track()=delete
Default constructor.
double RndmUniform()
Draw a random number uniformly distributed in the range [0, 1).
Definition Random.hh:14
void RndmDirection(double &dx, double &dy, double &dz, const double length=1.)
Draw a random (isotropic) direction vector.
Definition Random.hh:128
double RndmUniformPos()
Draw a random number uniformly distributed in the range (0, 1).
Definition Random.hh:17
DoubleAc pow(const DoubleAc &f, double p)
Definition DoubleAc.cpp:337
DoubleAc sqrt(const DoubleAc &f)
Definition DoubleAc.cpp:314